#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { createDbPool } from '../db.mjs';
import { buildExecutorLaunchPlan, createLlmProviderService } from '../llm-providers.mjs';

const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');

function loadEnvFile(filePath) {
  if (!fs.existsSync(filePath)) return;
  for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('#')) continue;
    const eq = trimmed.indexOf('=');
    if (eq < 0) continue;
    const key = trimmed.slice(0, eq).trim();
    const value = trimmed.slice(eq + 1).trim();
    if (!process.env[key]) process.env[key] = value;
  }
}

function parseArgs(argv) {
  if (argv[0] === '--help' || argv[0] === '-h') {
    return { help: true, executor: '' };
  }
  const [executor, ...rest] = argv;
  const out = {
    executor: String(executor ?? '').trim(),
    cwd: process.cwd(),
    mode: 'headless',
    instruction: '',
    purpose: 'default',
  };
  for (let i = 0; i < rest.length; i += 1) {
    const item = rest[i];
    if (item === '--cwd') out.cwd = String(rest[++i] ?? out.cwd);
    else if (item === '--mode') out.mode = String(rest[++i] ?? out.mode);
    else if (item === '--instruction') out.instruction = String(rest[++i] ?? out.instruction);
    else if (item === '--purpose') out.purpose = String(rest[++i] ?? out.purpose);
    else if (item === '--help' || item === '-h') out.help = true;
  }
  return out;
}

function printHelp() {
  console.log([
    'Usage:',
    '  node scripts/launch-executor.mjs <goose|aider|openhands> [--cwd <dir>] [--mode headless|serve] [--instruction <text>] [--purpose default]',
    '',
    'Examples:',
    '  node scripts/launch-executor.mjs aider --cwd /path/to/repo --instruction "fix login button"',
    '  node scripts/launch-executor.mjs openhands --mode serve --cwd /path/to/repo',
    '  node scripts/launch-executor.mjs openhands --mode headless --cwd /path/to/repo --instruction "add search"',
  ].join('\n'));
}

loadEnvFile(path.join(root, '.env'));
loadEnvFile(path.join(root, '.env.local'));

const parsed = parseArgs(process.argv.slice(2));
if (parsed.help || !parsed.executor) {
  printHelp();
  process.exit(parsed.help ? 0 : 1);
}

const pool = createDbPool();
const svc = createLlmProviderService(pool, {
  apiTarget: process.env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006',
  apiSecret: process.env.TKMIND_SERVER__SECRET_KEY ?? 'local-dev-secret',
});

const plan = await svc.getExecutorLaunchPlan(parsed.executor, {
  cwd: path.resolve(parsed.cwd),
  mode: parsed.mode,
  instruction: parsed.instruction,
  purpose: parsed.purpose,
  includeSecret: true,
});

if (!plan.ok) {
  console.error(plan.message ?? '执行器启动计划不可用');
  await pool.end();
  process.exit(1);
}

console.log(`==> ${plan.executorLabel ?? plan.executor} launch plan`);
console.log(`cwd: ${plan.cwd}`);
console.log(`cmd: ${plan.command} ${plan.args?.join(' ') ?? ''}`.trim());
if (plan.notes?.length) {
  for (const note of plan.notes) console.log(`note: ${note}`);
}

const child = spawn(plan.command, plan.args ?? [], {
  cwd: plan.cwd,
  env: { ...process.env, ...(plan.env ?? {}) },
  stdio: 'inherit',
});

const shutdown = (code = 0) => {
  child.kill('SIGTERM');
  setTimeout(() => process.exit(code), 200);
};

process.on('SIGINT', () => shutdown(0));
process.on('SIGTERM', () => shutdown(0));
child.on('exit', async (code, signal) => {
  await pool.end().catch(() => {});
  if (signal) {
    process.exit(0);
    return;
  }
  process.exit(code ?? 0);
});
child.on('error', async (err) => {
  console.error(err instanceof Error ? err.message : String(err));
  await pool.end().catch(() => {});
  process.exit(1);
});
