#!/usr/bin/env node
import {
  getConfigDirUsage,
  listRecentCrashes
} from "./chunk-WGJ7LE6T.js";
import {
  ProviderRegistry
} from "./chunk-5LK7H45B.js";
import {
  getStatsSnapshot,
  getTopFailingTools,
  getTopUsedTools,
  resetStats
} from "./chunk-PS6S4IIW.js";
import "./chunk-HIU2SH4V.js";
import {
  ConfigManager
} from "./chunk-WLMBMRIA.js";
import "./chunk-TZQHYZKT.js";
import {
  DEV_STATE_FILE_NAME,
  MEMORY_FILE_NAME,
  VERSION
} from "./chunk-2DVGTYZG.js";
import "./chunk-PDX44BCA.js";

// src/diagnostics/doctor-cli.ts
import { existsSync, statSync } from "fs";
import { join } from "path";
import { platform, release, arch } from "os";
function compareVersion(a, b) {
  const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
  const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
  const len = Math.max(pa.length, pb.length);
  for (let i = 0; i < len; i++) {
    const da = pa[i] ?? 0;
    const db = pb[i] ?? 0;
    if (da !== db) return da - db;
  }
  return 0;
}
async function checkNpmLatest() {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 3e3);
  try {
    const res = await fetch("https://registry.npmjs.org/jinzd-ai-cli/latest", {
      signal: controller.signal,
      headers: { accept: "application/json" }
    });
    if (!res.ok) {
      return { latest: null, status: "skipped", reason: `HTTP ${res.status}` };
    }
    const data = await res.json();
    const latest = typeof data.version === "string" ? data.version : null;
    if (!latest) return { latest: null, status: "skipped", reason: "no version in response" };
    const cmp = compareVersion(latest, VERSION);
    return { latest, status: cmp > 0 ? "outdated" : "up-to-date" };
  } catch (err) {
    const reason = err?.name === "AbortError" ? "timeout (3s)" : err?.message ?? "network error";
    return { latest: null, status: "skipped", reason };
  } finally {
    clearTimeout(timer);
  }
}
async function collect() {
  const config = new ConfigManager();
  const registry = new ProviderRegistry();
  await registry.initialize(
    (id) => config.getApiKey(id),
    (id) => ({
      baseUrl: config.get("customBaseUrls")[id],
      timeout: config.get("timeouts")[id]
    }),
    config.get("customProviders")
  );
  const providers = registry.listAll().map((p) => ({
    id: p.id,
    displayName: p.displayName,
    configured: p.configured
  }));
  const configDir = config.getConfigDir();
  const checkFile = (label, path) => {
    const exists = existsSync(path);
    let sizeBytes = null;
    if (exists) {
      try {
        sizeBytes = statSync(path).size;
      } catch {
      }
    }
    return { label, path, exists, sizeBytes };
  };
  const npmCheck = await checkNpmLatest();
  return {
    version: VERSION,
    npmCheck,
    node: process.version,
    platform: `${platform()} ${release()} (${arch()})`,
    configDir,
    providers,
    files: [
      checkFile("config.json", join(configDir, "config.json")),
      checkFile("memory.md", join(configDir, MEMORY_FILE_NAME)),
      checkFile("dev-state.md", join(configDir, DEV_STATE_FILE_NAME))
    ],
    recentCrashes: listRecentCrashes(5, configDir),
    diskUsage: getConfigDirUsage(configDir),
    toolStats: (() => {
      const all = getStatsSnapshot();
      const totalCalls = all.reduce((a, b) => a + b.calls, 0);
      const totalFailures = all.reduce((a, b) => a + b.failures, 0);
      return {
        totalCalls,
        totalFailures,
        topUsed: getTopUsedTools(5),
        topFailing: getTopFailingTools(5)
      };
    })()
  };
}
function formatBytes(n) {
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
  return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
}
async function runDoctorCli(options = {}) {
  if (options.resetStats) {
    resetStats();
    process.stdout.write("Tool stats reset.\n");
    return;
  }
  const report = await collect();
  if (options.json) {
    process.stdout.write(JSON.stringify(report, null, 2) + "\n");
    return;
  }
  const B = "\x1B[1m";
  const D = "\x1B[2m";
  const G = "\x1B[32m";
  const Y = "\x1B[33m";
  const R_ = "\x1B[31m";
  const RESET = "\x1B[0m";
  const out = [];
  out.push("");
  out.push(`${B}\u{1FA7A} AI-CLI Health Check${RESET}`);
  out.push("");
  const nc = report.npmCheck;
  let versionLine = `  version:  ${report.version}`;
  if (nc.status === "up-to-date") {
    versionLine += `  ${D}(latest on npm)${RESET}`;
  } else if (nc.status === "outdated" && nc.latest) {
    versionLine += `  ${Y}\u2192 ${nc.latest} available${RESET}  ${D}(npm i -g jinzd-ai-cli@latest)${RESET}`;
  } else if (nc.status === "skipped") {
    versionLine += `  ${D}(npm check skipped: ${nc.reason ?? "unknown"})${RESET}`;
  }
  out.push(versionLine);
  out.push(`  node:     ${report.node}`);
  out.push(`  platform: ${report.platform}`);
  out.push(`  config:   ${report.configDir}`);
  out.push("");
  out.push(`${B}API Keys:${RESET}`);
  for (const p of report.providers) {
    const icon = p.configured ? `${G}\u2713${RESET}` : `${D}\u25CB${RESET}`;
    const status = p.configured ? `${G}configured${RESET}` : `${D}not configured${RESET}`;
    out.push(`  ${icon}  ${p.id.padEnd(14)} ${status}`);
  }
  out.push("");
  out.push(`${B}Config Files:${RESET}`);
  for (const f of report.files) {
    const icon = f.exists ? `${G}\u2713${RESET}` : `${D}\u2013${RESET}`;
    const extra = f.exists && f.sizeBytes !== null ? ` ${D}(${formatBytes(f.sizeBytes)})${RESET}` : "";
    out.push(`  ${icon}  ${f.label.padEnd(14)} ${f.exists ? f.path : `${D}(not found)${RESET}`}${extra}`);
  }
  out.push("");
  out.push(`${B}Recent Crashes (last 5):${RESET}`);
  if (report.recentCrashes.length === 0) {
    out.push(`  ${D}(none \u2014 clean!)${RESET}`);
  } else {
    for (const c of report.recentCrashes) {
      out.push(`  ${R_}\u2717${RESET}  ${c.timestamp}  ${Y}${c.kind}${RESET}`);
      out.push(`     ${D}${c.message}${RESET}`);
      out.push(`     ${D}${c.file}${RESET}`);
    }
  }
  out.push("");
  out.push(`${B}Tool Usage:${RESET}`);
  const ts = report.toolStats;
  out.push(`  total: ${ts.totalCalls} calls \xB7 ${ts.totalFailures} failures (${ts.totalCalls ? (ts.totalFailures * 100 / ts.totalCalls).toFixed(1) : "0.0"}%)`);
  if (ts.topUsed.length > 0) {
    out.push(`  ${D}top used:${RESET}`);
    for (const t of ts.topUsed) {
      const avg = t.calls ? (t.totalDurationMs / t.calls).toFixed(0) : "0";
      out.push(`    ${t.name.padEnd(20)} ${String(t.calls).padStart(6)} calls \xB7 avg ${avg}ms`);
    }
  }
  if (ts.topFailing.length > 0) {
    out.push(`  ${D}top failing:${RESET}`);
    for (const t of ts.topFailing) {
      const rate = (t.failures * 100 / Math.max(1, t.calls)).toFixed(1);
      out.push(`    ${R_}${t.name.padEnd(20)}${RESET} ${t.failures}/${t.calls} (${rate}%)  ${D}${t.lastFailureMessage ?? ""}${RESET}`.slice(0, 200));
    }
  }
  out.push("");
  const dirAnnotations = {
    models: "semantic search model  \u2192  aicli: /index semantic-clear",
    index: "symbol index           \u2192  aicli: /index clear",
    history: "conversation history   \u2192  auto-pruned after 30 days",
    logs: "crash logs",
    "memory-index": "chat memory index     \u2192  aicli: /memory index-clear"
  };
  out.push(`${B}Disk Usage (~/.aicli):${RESET}`);
  out.push(`  total: ${formatBytes(report.diskUsage.totalBytes)}`);
  for (const e of report.diskUsage.entries.slice(0, 10)) {
    const note = dirAnnotations[e.name];
    const noteStr = note ? `  ${D}\xB7  ${note}${RESET}` : "";
    out.push(`    ${e.name.padEnd(18)} ${formatBytes(e.bytes)}${noteStr}`);
  }
  out.push("");
  out.push(`${G}\u2713 Health check complete${RESET}`);
  out.push("");
  process.stdout.write(out.join("\n"));
}
export {
  runDoctorCli
};
