import { NextResponse } from "next/server";
import { readFile, readdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import path from "node:path";
import os from "node:os";
import { config, hermesHome } from "@/lib/config";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

// Server-side mirror of pack/Check My Setup.command — the Home "Getting
// Started" checklist. Read-only, fast, and it NEVER throws: every probe is
// wrapped, and the handler always answers 200 with usable JSON.
//
// Bonus: ?guide=<NN-name.md> serves the matching pack/install guide as
// markdown (whitelisted filenames only), so checklist rows can deep-link
// a pretty in-app guide via /setup-guide/<doc>.

export interface SetupCheck {
  id: string;
  label: string;
  ok: boolean;
  optional: boolean;
  fix: string;      // one line: what to do when not connected
  guide: string | null; // install guide filename, e.g. "4-HERMES.md"
}

const PAPERCLIP_PROBE_MS = 1500;

// Only these guides may ever be served — no path tricks possible.
const GUIDE_WHITELIST = new Set([
  "0-HOW-IT-ALL-WORKS.md",
  "1-CORE-DASHBOARD.md",
  "2-VOICE-BUILDING.md",
  "3-JARVIS-VOICE.md",
  "4-HERMES.md",
  "5-FREE-CLAUDE-CODE.md",
  "6-PAPERCLIP.md",
  "7-AGENT-CLIS.md",
  "8-TROUBLESHOOTING.md",
  "11-MEMORY-OBSIDIAN.md",
]);

async function serveGuide(doc: string): Promise<Response> {
  if (!GUIDE_WHITELIST.has(doc)) {
    return new Response("# Guide not found\n\nThat guide is not part of the setup checklist.", {
      status: 404, headers: { "Content-Type": "text/markdown; charset=utf-8" },
    });
  }
  // The app runs from pack/source, guides live in pack/install.
  const candidates = [
    path.resolve(process.cwd(), "..", "install", doc),
    path.resolve(process.cwd(), "install", doc),
  ];
  for (const file of candidates) {
    try {
      const content = await readFile(file, "utf8");
      return new Response(content, { headers: { "Content-Type": "text/markdown; charset=utf-8" } });
    } catch { /* try next */ }
  }
  return new Response(
    `# Guide not on disk\n\nLook for **install/${doc}** inside your MissionMate pack folder.`,
    { status: 404, headers: { "Content-Type": "text/markdown; charset=utf-8" } },
  );
}

async function hasProfileEnvKey(re: RegExp): Promise<boolean> {
  try {
    const base = path.join(hermesHome(), "profiles");
    const profiles = await readdir(base);
    for (const profile of profiles) {
      try {
        const txt = await readFile(path.join(base, profile, ".env"), "utf8");
        if (re.test(txt)) return true;
      } catch { /* next profile */ }
    }
  } catch { /* no profiles dir */ }
  return false;
}

async function probeLocal(url: string): Promise<boolean> {
  const ac = new AbortController();
  const timer = setTimeout(() => ac.abort(), PAPERCLIP_PROBE_MS);
  try {
    await fetch(url, { signal: ac.signal, cache: "no-store" });
    return true; // any response at all means something is listening
  } catch {
    return false;
  } finally {
    clearTimeout(timer);
  }
}

async function buildChecks(): Promise<SetupCheck[]> {
  const [voiceKey, hermesKey, paperclip] = await Promise.all([
    hasProfileEnvKey(/^ELEVENLABS_API_KEY=.+/m),
    hasProfileEnvKey(/^OPENROUTER_API_KEY=.+/m),
    probeLocal("http://localhost:3100"),
  ]);
  const hermesInstalled =
    Boolean(config.hermes) || existsSync(path.join(os.homedir(), ".local", "bin", "hermes"));
  const vaultOk = Boolean(config.vaultRoot && existsSync(config.vaultRoot));

  return [
    { id: "node", label: `Node.js ${process.version}`, ok: true, optional: false,
      fix: "Install Node from nodejs.org, then relaunch.", guide: "1-CORE-DASHBOARD.md" },
    { id: "dashboard", label: "Dashboard running", ok: true, optional: false,
      fix: "Double-click Start MissionMate.command.", guide: "1-CORE-DASHBOARD.md" },
    { id: "vault", label: "Obsidian vault connected", ok: vaultOk, optional: true,
      fix: "Point vaultRoot at your vault in ~/.agentic-os/config.json.", guide: "11-MEMORY-OBSIDIAN.md" },
    { id: "hermes", label: "Hermes agent installed", ok: hermesInstalled, optional: true,
      fix: "Install the Hermes agent so chat, voice and kanban come alive.", guide: "4-HERMES.md" },
    { id: "hermes-key", label: "OpenRouter key (Hermes can think)", ok: hermesKey, optional: true,
      fix: "Add OPENROUTER_API_KEY to your Hermes profile.", guide: "4-HERMES.md" },
    { id: "voice-key", label: "Jarvis voice key", ok: voiceKey, optional: true,
      fix: "Add your ElevenLabs key. The free tier works.", guide: "3-JARVIS-VOICE.md" },
    { id: "claude", label: "Claude Code installed", ok: Boolean(config.claude), optional: true,
      fix: "Install Claude Code from claude.com/claude-code.", guide: "7-AGENT-CLIS.md" },
    { id: "paperclip", label: "Paperclip company running", ok: paperclip, optional: true,
      fix: "Start your Paperclip agent company. Totally optional.", guide: "6-PAPERCLIP.md" },
  ];
}

export async function GET(req: Request) {
  try {
    const guide = new URL(req.url).searchParams.get("guide");
    if (guide) return serveGuide(guide);

    const checks = await buildChecks();
    const userName = config.userName && config.userName !== "You" ? config.userName : null;
    return NextResponse.json({ ok: true, userName, checks, generatedAt: Date.now() });
  } catch (e) {
    // Contract: never throw, never 500 — hand back an empty-but-valid shape.
    return NextResponse.json(
      { ok: false, userName: null, checks: [], generatedAt: Date.now(), error: String(e) },
      { status: 200 },
    );
  }
}
