import { existsSync } from "node:fs";
import { join } from "node:path";
import { Database } from "bun:sqlite";
import { paths } from "../../lifecycle/paths.ts";
import { isAlive, readPid } from "../../lifecycle/pid.ts";
import { anthropicProvider } from "../../provider/anthropic.ts";
import { openAIProvider } from "../../provider/openai.ts";
import { toKeyVal } from "../renderAI.ts";
import { initStatsSchema } from "../../stats/schema.ts";
import { prunePollutedFiles } from "../../stats/writer.ts";
import { countPhantoms, phantomFilterSql } from "../../stats/phantom.ts";
import { backfillCompressedDrift } from "../../stats/migrate.ts";
import { CodexInstaller } from "../../install/codex/CodexInstaller.ts";

const registeredProviders = [anthropicProvider, openAIProvider];

/** True when `codex` binary is on PATH (uses Bun.spawnSync — no child_process import). */
function codexOnPath(): boolean {
  try {
    const r = Bun.spawnSync(["codex", "--version"], { stdout: "pipe", stderr: "pipe" });
    return r.exitCode === 0;
  } catch {
    return false;
  }
}

export async function cmdDoctor(flags: Record<string, string | true>, verbose = false): Promise<number> {
  // Handle --backfill-compressed [--dry-run]
  if (flags["backfill-compressed"] === true) {
    const dryRun = flags["dry-run"] === true;
    const dbPath = join(paths.home(), "stats.db");
    if (!existsSync(dbPath)) {
      console.log(toKeyVal({ candidates: 0, updated: 0, skipped: 0 }));
      return 0;
    }
    const db = new Database(dbPath);
    initStatsSchema(db);
    const r = backfillCompressedDrift(db, { dryRun });
    db.close();
    console.log(toKeyVal({ candidates: r.candidates, updated: r.updated, skipped: r.skipped, dryRun }));
    return 0;
  }

  // Handle --prune-polluted [--days N] [--dry-run]
  if (flags["prune-polluted"] === true) {
    const daysStr = typeof flags["days"] === "string" ? flags["days"] : "30";
    const days = Math.max(1, parseInt(daysStr, 10) || 30);
    const dryRun = flags["dry-run"] === true;
    const result = prunePollutedFiles(paths.root(), { olderThanDays: days, dryRun });
    console.log("polluted-stats cleanup:");
    console.log(`  candidates: ${result.candidates.length}   (matching filename pattern)`);
    console.log(`  pruned:     ${result.pruned.length}   (older than ${days}d${dryRun ? ", dry-run" : ""})`);
    console.log(`  skipped:    ${result.skipped.length}   (too recent or unparseable)`);
    if (result.pruned.length > 0) {
      console.log("\n" + (dryRun ? "would remove:" : "removed:"));
      for (const name of result.pruned) console.log(`  ${name}`);
    }
    if (result.skipped.length > 0) {
      console.log("\nskipped:");
      for (const s of result.skipped) console.log(`  ${s.name}  (${s.reason})`);
    }
    return 0;
  }

  // Handle --purge-phantom-rows --yes
  if (flags["purge-phantom-rows"] === true) {
    if (flags["yes"] !== true) {
      console.error("err=purge requires --yes flag to confirm");
      return 1;
    }
    const statsDir = paths.home();
    const dbPath = join(statsDir, "stats.db");
    if (!existsSync(dbPath)) {
      console.log(toKeyVal({ purged: 0, phantom_before: 0 }));
      return 0;
    }
    const db = new Database(dbPath);
    initStatsSchema(db);
    const count = purgePhantomRows(db);
    db.close();
    console.log(toKeyVal({ purged: count }));
    return 0;
  }

  const codexPresent = codexOnPath();

  if (!verbose) {
    const pid = readPid(paths.pidFile());
    const proxyUp = pid !== null && isAlive(paths.pidFile());
    const homeOk = existsSync(paths.home());
    const rootOk = existsSync(paths.root());
    const providerList = registeredProviders.map((p) => p.id).join(",") || "none";
    // Count phantom rows if stats DB exists
    const statsDir = paths.home();
    const dbPath = join(statsDir, "stats.db");
    let phantomCount = 0;
    let phantomRate = "0/0 (0%)";
    let phantomBreakdown = "aborted:0,missing-baseline:0,corrupt-usage:0";
    if (existsSync(dbPath)) {
      const rwDb = new Database(dbPath);
      initStatsSchema(rwDb);
      rwDb.close();
      const db = new Database(dbPath, { readonly: true });
      const counts = countPhantoms(db);
      phantomCount = counts.phantomTotal;
      const pct = counts.totalRows > 0 ? ((counts.phantomTotal / counts.totalRows) * 100).toFixed(1) : "0";
      phantomRate = `${counts.phantomTotal}/${counts.totalRows} (${pct}%)`;
      phantomBreakdown = `aborted:${counts.aborted},missing-baseline:${counts.missingBaseline},corrupt-usage:${counts.corruptUsage}`;
      db.close();
    }
    console.log(toKeyVal({
      proxy: proxyUp ? "up" : "down",
      home: homeOk ? "ok" : "err",
      root: rootOk ? "ok" : "err",
      providers: providerList,
      phantom_rows: phantomCount,
      phantom_rate: phantomRate,
      phantom_breakdown: phantomBreakdown,
      codex: codexPresent ? "present" : "absent",
    }));
    return proxyUp ? 0 : 1;
  }

  const checks: Array<{ ok: boolean; msg: string }> = [];
  const pid = readPid(paths.pidFile());
  if (pid !== null && isAlive(paths.pidFile())) {
    checks.push({ ok: true, msg: `proxy running (pid ${pid})` });
  } else {
    checks.push({ ok: true, msg: "proxy not running" });
  }
  checks.push({ ok: true, msg: `home: ${paths.home()}` });
  checks.push({ ok: existsSync(paths.root()) || true, msg: `root: ${paths.root()}` });
  for (const c of checks) console.log(`${c.ok ? "OK" : "FAIL"} ${c.msg}`);

  console.log("\nProviders:");
  for (const p of registeredProviders) {
    console.log(
      `  ${(p.id as string).padEnd(12)} cache markers: ${p.supportsCacheMarkers ? "yes" : "no"}  upstream: ${p.defaultUpstreamUrl}`,
    );
  }

  // Codex CLI health
  const codexInstaller = new CodexInstaller();
  const codexHealth = await codexInstaller.check();
  console.log("\nCodex CLI:");
  if (codexHealth.findings.length === 0) {
    console.log("  OK  codex configured");
  } else {
    for (const f of codexHealth.findings) {
      const tag = f.level === "error" ? "FAIL" : "WARN";
      const remedy = f.remediation ? `  (${f.remediation})` : "";
      console.log(`  ${tag}  ${f.message}${remedy}`);
    }
  }

  return checks.every((c) => c.ok) ? 0 : 1;
}

// ── phantom-row helpers ────────────────────────────────────────────────────

/** Count requests rows matching the shared phantom predicate. */
export function countPhantomRows(db: Database): number {
  return countPhantoms(db).phantomTotal;
}

/** Delete requests rows matching the shared phantom predicate. Returns count deleted. */
export function purgePhantomRows(db: Database): number {
  const before = countPhantomRows(db);
  db.run(`DELETE FROM requests WHERE NOT (${phantomFilterSql})`);
  return before;
}

/**
 * Open the stats DB at `dir`, count phantom rows, and optionally purge them.
 * Returns { phantomCount, purged }.
 */
export function runPhantomCheck(
  dir: string,
  opts: { purge?: boolean } = {},
): { phantomCount: number; purged: number } {
  const dbPath = join(dir, "stats.db");
  const db = new Database(dbPath);
  initStatsSchema(db);
  const phantomCount = countPhantomRows(db);
  let purged = 0;
  if (opts.purge && phantomCount > 0) {
    purged = purgePhantomRows(db);
  }
  db.close();
  return { phantomCount, purged };
}
