#!/usr/bin/env node
// Interactive (and --yes non-interactive) setup wizard. Generates a minimal
// config.yaml (+ .env PORT) for a single LLM connection. Run before building:
//   npm run setup
import { createInterface } from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
import {
  existsSync, readFileSync, openSync, writeFileSync, fsyncSync, closeSync,
  renameSync, copyFileSync, chmodSync, unlinkSync,
} from 'node:fs';
import { join } from 'node:path';
import {
  parseEndpoint, buildWorkerEntry, renderConfigYaml, renderDotenv,
  parseAnswersFromEnv, probeModels,
} from './setup-lib.mjs';

const ROOT = process.cwd();
const CONFIG_PATH = join(ROOT, 'config.yaml');
const ENV_PATH = join(ROOT, '.env');

const maskKey = (k) => (k ? `${String(k).slice(0, 6)}****` : '');

function atomicWrite(path, content, mode) {
  const tmp = `${path}.${process.pid}.tmp`;
  const fd = openSync(tmp, 'w', mode);
  try { writeFileSync(fd, content); fsyncSync(fd); } finally { closeSync(fd); }
  try { chmodSync(tmp, mode); } catch (e) { console.warn(`⚠ chmod ${tmp} failed (${e.message}); continuing`); }
  try { renameSync(tmp, path); } catch (e) { try { unlinkSync(tmp); } catch {} throw e; }
}

function backupExistingConfig() {
  const bak = `${CONFIG_PATH}.bak`;
  copyFileSync(CONFIG_PATH, bak);                 // copy (not rename) so original survives a later failure
  try { chmodSync(bak, 0o600); } catch (e) { console.warn(`⚠ chmod ${bak} failed (${e.message})`); }
  console.log(`  既存 config.yaml を ${bak} (0600) に退避しました。`);
}

function writeOutputs({ worker, port }) {
  // config.yaml is always 0600 (may hold an api_key).
  atomicWrite(CONFIG_PATH, renderConfigYaml(worker), 0o600);
  const existingEnv = existsSync(ENV_PATH) ? readFileSync(ENV_PATH, 'utf-8') : '';
  atomicWrite(ENV_PATH, renderDotenv(existingEnv, { port }), 0o644);
}

function printDone({ port, worker }) {
  console.log('');
  console.log('✅ config.yaml を生成しました（詳細設定は config.yaml.example か Settings UI を参照）。');
  if (worker.api_key) console.log(`   gateway api_key: ${maskKey(worker.api_key)}（config.yaml に保存、権限 0600）`);
  console.log('次の手順:');
  console.log('  scripts/build-all.sh');
  console.log('  scripts/server.sh start');
  console.log(`  ブラウザで http://localhost:${port}`);
}

async function runNonInteractive(force) {
  const { answers, error } = parseAnswersFromEnv(process.env);
  if (error) { console.error(`setup --yes: ${error}`); process.exit(1); }
  if (existsSync(CONFIG_PATH) && !force) {
    console.error('setup --yes: config.yaml already exists; pass --force to overwrite.');
    process.exit(1);
  }
  const probe = await probeModels(answers);   // non-fatal: model already provided via env
  if (!probe.ok) console.warn(`⚠ LLM probe failed (${probe.error}); continuing (SETUP_MODEL is set).`);
  const worker = buildWorkerEntry({
    connectionType: answers.connectionType, endpoint: answers.endpoint, model: answers.model, apiKey: answers.apiKey,
  });
  if (existsSync(CONFIG_PATH)) backupExistingConfig();
  writeOutputs({ worker, port: answers.port });
  printDone({ port: answers.port, worker });
}

async function ask(rl, question, def) {
  const suffix = def !== undefined && def !== '' ? ` [${def}]` : '';
  const a = (await rl.question(`${question}${suffix}: `)).trim();
  return a || (def ?? '');
}

async function runInteractive() {
  const rl = createInterface({ input, output });
  try {
    if (existsSync(CONFIG_PATH)) {
      const yn = (await ask(rl, '既存の config.yaml を上書きしますか? 既存は config.yaml.bak に退避します (y/N)', 'N'));
      if (!/^y(es)?$/i.test(yn)) { console.log('中止しました（変更なし）。'); return; }
    }

    // connection type
    let connectionType = 'direct';
    const ct = await ask(rl, '接続タイプ  1) direct (Ollama/vLLM 等)  2) aao_gateway (別 MAESTRO Gateway)', '1');
    connectionType = ct === '2' || ct === 'aao_gateway' ? 'aao_gateway' : 'direct';

    // endpoint
    let endpoint, base;
    const defEndpoint = connectionType === 'direct' ? 'http://localhost:11434/v1' : '';
    const hint = connectionType === 'aao_gateway' ? '（例: http://gateway-host:9876/v1）' : '';
    for (;;) {
      const raw = await ask(rl, `LLM endpoint URL${hint}`, defEndpoint);
      const r = parseEndpoint(raw);
      if (r.error) { console.log(`  ⚠ ${r.error}`); continue; }
      ({ endpoint, base } = r);
      break;
    }

    // api key (gateway only)
    let apiKey;
    if (connectionType === 'aao_gateway') {
      for (;;) {
        apiKey = await ask(rl, 'gateway API キー (sk-aao-...)', '');
        if (apiKey) break;
        console.log('  ⚠ aao_gateway では API キーが必須です。');
      }
    }

    // probe + model
    console.log('  接続を確認しています...');
    const probe = await probeModels({ endpoint, base, apiKey });
    let model;
    if (probe.ok && probe.models.length > 0) {
      console.log('  利用可能なモデル:');
      probe.models.forEach((m, i) => console.log(`    ${i + 1}) ${m}`));
      for (;;) {
        const sel = await ask(rl, `モデルを選択 (1-${probe.models.length}, または名前を直接入力)`, '1');
        const idx = Number(sel);
        if (Number.isInteger(idx) && idx >= 1 && idx <= probe.models.length) { model = probe.models[idx - 1]; break; }
        if (sel) { model = sel; break; }
      }
    } else {
      if (!probe.ok) console.log(`  ⚠ 接続できませんでした (${probe.error})。後で config を直せます。`);
      else console.log('  ⚠ モデル一覧が空でした。');
      for (;;) { model = await ask(rl, 'モデル名を手入力', ''); if (model) break; }
    }

    // server port
    let port;
    for (;;) {
      const p = Number(await ask(rl, 'MAESTRO サーバーの listen port', '9876'));
      if (Number.isInteger(p) && p >= 1 && p <= 65535) { port = p; break; }
      console.log('  ⚠ port は 1-65535 の整数で入力してください。');
    }

    const worker = buildWorkerEntry({ connectionType, endpoint, model, apiKey });
    if (existsSync(CONFIG_PATH)) backupExistingConfig();
    writeOutputs({ worker, port });
    printDone({ port, worker });
  } finally {
    rl.close();
  }
}

async function main() {
  const argv = process.argv.slice(2);
  const yes = argv.includes('--yes');
  const force = argv.includes('--force');
  if (yes) await runNonInteractive(force);
  else await runInteractive();
}

main().catch((e) => { console.error(`setup failed: ${e && e.message ? e.message : e}`); process.exit(1); });
