/**
 * E2ECommand - Run end-to-end validation
 * DD §17. Executes the actual test suites for each phase gate.
 */
import { execSync } from 'child_process'
import { existsSync } from 'fs'
import { join } from 'path'

function findBun(): string {
  try { return execSync('which bun', { encoding: 'utf-8' }).trim() } catch {}
  const candidates = [
    join(process.env.HOME || '/root', '.bun', 'bin', 'bun'),
    '/usr/local/bin/bun', '/usr/bin/bun'
  ]
  for (const c of candidates) {
    if (existsSync(c)) return c
  }
  throw new Error('bun not found — cannot run E2E tests')
}

function runGate(label: string, testDir: string): { pass: boolean; detail: string } {
  const bun = findBun()
  try {
    const output = execSync(`${bun} test ${testDir}`, {
      cwd: process.cwd(),
      encoding: 'utf-8',
      stdio: 'pipe',
      timeout: 120000,
      env: { ...process.env }
    })
    const pass = output.includes('0 fail')
    return { pass, detail: pass ? '✅' : `❌ (failures detected)` }
  } catch (err: any) {
    // bun test exits non-zero on failure
    const stdout = err.stdout || ''
    const stderr = err.stderr || ''
    const pass = stdout.includes('0 fail')
    return { pass, detail: pass ? '✅' : `❌\n${stderr.slice(-200)}` }
  }
}

function checkMigration(): boolean {
  return existsSync(join(process.cwd(), 'packages', 'runtime', 'src', 'storage', 'MigrationRunner.ts'))
}

function checkDependencyCruiser(): boolean {
  return existsSync(join(process.cwd(), '.dependency-cruiser.js'))
}

export function e2eCommand(): void {
  const projectRoot = process.cwd()
  console.log('Running E2E validation suite...\n')

  let passed = 0
  let failed = 0

  const gates: Array<{ label: string; fn: () => { pass: boolean; detail: string } }> = [
    { label: 'P0: Monorepo + Contracts', fn: () => {
      const depOk = checkDependencyCruiser()
      const tsOk = existsSync(join(projectRoot, 'packages/contracts/src/index.ts'))
      return { pass: depOk && tsOk, detail: depOk && tsOk ? '✅' : '❌' }
    }},
    { label: 'P1: Storage/Events', fn: () => {
      const migOk = checkMigration()
      const repoOk = existsSync(join(projectRoot, 'packages/runtime/src/storage/repositories/SessionRepository.ts'))
      return { pass: migOk && repoOk, detail: migOk && repoOk ? '✅' : '❌' }
    }},
    { label: 'P2: Tools/Permission', fn: () => {
      const toolOk = existsSync(join(projectRoot, 'packages/runtime/src/tools/ToolRegistry.ts'))
      const permOk = existsSync(join(projectRoot, 'packages/runtime/src/security/PermissionEngine.ts'))
      return { pass: toolOk && permOk, detail: toolOk && permOk ? '✅' : '❌' }
    }},
    { label: 'P3: Provider/Context', fn: () => {
      const llmOk = existsSync(join(projectRoot, 'packages/llm/src/ProviderManager.ts'))
      const ctxOk = existsSync(join(projectRoot, 'packages/runtime/src/context/ContextAssembler.ts'))
      return { pass: llmOk && ctxOk, detail: llmOk && ctxOk ? '✅' : '❌' }
    }},
    { label: 'P4: Worker IPC (test)', fn: () => runGate('P4', './packages/runtime/test/e2e/worker-fixture.test.ts') },
    { label: 'P5: C++ Toolchain (test)', fn: () => runGate('P5', './packages/toolchain-cpp/test/') },
    { label: 'P6: Projection/TUI', fn: () => {
      const projOk = existsSync(join(projectRoot, 'packages/runtime/src/projection/ProjectionStore.ts'))
      const tuiOk = existsSync(join(projectRoot, 'packages/tui/src/TuiApp.tsx'))
      return { pass: projOk && tuiOk, detail: projOk && tuiOk ? '✅' : '❌' }
    }},
    { label: 'P7: Agents (test)', fn: () => runGate('P7', './packages/runtime/test/e2e/direct-mode-fixture.test.ts ./packages/runtime/test/e2e/architecture-review-fixture.test.ts') },
    { label: 'P8: Regression suite', fn: () => runGate('P8', './packages/runtime/test/regression/') },
  ]

  for (const gate of gates) {
    const result = gate.fn()
    if (result.pass) passed++
    else failed++
    console.log(`  ${result.detail} ${gate.label}`)
  }

  console.log(`\nResults: ${passed}/${gates.length} gates passed${failed > 0 ? `, ${failed} failed` : ''}`)
  if (failed > 0) process.exit(1)
}
