/**
 * Forge Config — Auto-detection
 *
 * Detects project language, framework, test runner, and build tools
 * to configure the Forge daemon automatically.
 */

import { readFile, stat } from 'fs/promises'
import { join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import type { DetectedProjectConfig, ForgeConfig } from './types.js'

/** Default forge configuration */
export const DEFAULT_FORGE_CONFIG: ForgeConfig = {
  watch: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.js', 'src/**/*.jsx', 'test/**/*', 'tests/**/*'],
  testCommand: 'npm test',
  incremental: true,
  maxConcurrency: 2,
  exclude: ['node_modules', '.git', 'dist', 'build', 'coverage'],
}

/**
 * Auto-detect project configuration from the current working directory.
 */
export async function detectProjectConfig(): Promise<DetectedProjectConfig> {
  const cwd = getCwd()
  const config: DetectedProjectConfig = {
    language: 'unknown',
    packageManager: 'npm',
    testCommand: 'npm test',
    sourcePatterns: ['src/**/*'],
    testPatterns: ['test/**/*', 'tests/**/*', '**/*.test.*', '**/*.spec.*'],
    extension: '.ts',
  }

  // Check for package.json (Node.js/TypeScript/JavaScript)
  try {
    const pkgPath = join(cwd, 'package.json')
    const pkgContent = await readFile(pkgPath, 'utf-8')
    const pkg = JSON.parse(pkgContent)

    config.packageManager = await detectPackageManager(cwd)

    // Detect test command from scripts
    if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') {
      config.testCommand = `${config.packageManager} test`
    }

    if (pkg.scripts?.lint) {
      config.lintCommand = `${config.packageManager} run lint`
    }

    if (pkg.scripts?.['type-check'] || pkg.scripts?.typecheck) {
      config.typeCheckCommand = `${config.packageManager} run type-check 2>/dev/null || ${config.packageManager} run typecheck`
    } else {
      // Check if TypeScript is present
      try {
        const tsconfigPath = join(cwd, 'tsconfig.json')
        await stat(tsconfigPath)
        config.language = 'typescript'
        config.typeCheckCommand = 'npx tsc --noEmit'
        config.extension = '.ts'
      } catch {
        config.language = 'javascript'
        config.extension = '.js'
      }
    }

    // Detect framework
    const deps = { ...pkg.dependencies, ...pkg.devDependencies }
    if (deps.react || deps.next) config.framework = 'react'
    if (deps.express || deps.fastify || deps.koa) config.framework = config.framework ? `${config.framework}/node` : 'node'
    if (deps.next) config.framework = 'next'
    if (deps.vue || deps.nuxt) config.framework = 'vue'

    // Detect test framework
    if (deps.jest || deps['@jest/globals']) {
      config.testCommand = 'npx jest'
    }
    if (deps.vitest) {
      config.testCommand = 'npx vitest run'
    }
    if (deps.pytest) {
      // Won't happen in package.json but future-proofing
      config.testCommand = 'pytest'
    }
  } catch {
    // No package.json — check for other project types
  }

  // Check for Python project
  try {
    const pyprojectPath = join(cwd, 'pyproject.toml')
    await stat(pyprojectPath)
    if (config.language === 'unknown') {
      config.language = 'python'
      config.packageManager = 'pip'
      config.testCommand = 'pytest'
      config.extension = '.py'
      config.sourcePatterns = ['src/**/*.py', '**/*.py']
      config.testPatterns = ['tests/**/*.py', 'test_*.py', '*_test.py']
    }
  } catch {
    // Not a Python project
  }

  // Check for Go project
  try {
    const goModPath = join(cwd, 'go.mod')
    await stat(goModPath)
    if (config.language === 'unknown') {
      config.language = 'go'
      config.packageManager = 'go'
      config.testCommand = 'go test ./...'
      config.extension = '.go'
      config.sourcePatterns = ['**/*.go']
      config.testPatterns = ['**/*_test.go']
    }
  } catch {
    // Not a Go project
  }

  // Check for Rust project
  try {
    const cargoPath = join(cwd, 'Cargo.toml')
    await stat(cargoPath)
    if (config.language === 'unknown') {
      config.language = 'rust'
      config.packageManager = 'cargo'
      config.testCommand = 'cargo test'
      config.lintCommand = 'cargo clippy'
      config.typeCheckCommand = 'cargo check'
      config.extension = '.rs'
      config.sourcePatterns = ['src/**/*.rs']
      config.testPatterns = ['tests/**/*.rs', 'src/**/*.rs'] // Rust tests are inline
    }
  } catch {
    // Not a Rust project
  }

  return config
}

/**
 * Convert detected config to ForgeConfig.
 */
export function detectedToForgeConfig(detected: DetectedProjectConfig): ForgeConfig {
  return {
    watch: [...detected.sourcePatterns, ...detected.testPatterns],
    testCommand: detected.testCommand,
    lintCommand: detected.lintCommand,
    typeCheckCommand: detected.typeCheckCommand,
    incremental: true,
    maxConcurrency: 2,
    exclude: ['node_modules', '.git', 'dist', 'build', 'coverage', 'target'],
  }
}

/**
 * Detect the package manager in use.
 */
async function detectPackageManager(cwd: string): Promise<string> {
  try {
    await stat(join(cwd, 'bun.lockb'))
    return 'bun'
  } catch {
    // Not bun
  }

  try {
    await stat(join(cwd, 'pnpm-lock.yaml'))
    return 'pnpm'
  } catch {
    // Not pnpm
  }

  try {
    await stat(join(cwd, 'yarn.lock'))
    return 'yarn'
  } catch {
    // Not yarn
  }

  return 'npm'
}
