import { Router, type IRouter } from "express";
import { execFile } from "child_process";
import { promisify } from "util";

const execFileAsync = promisify(execFile);
const router: IRouter = Router();

interface NodeStatus {
  timestamp: string;
  lnbits: LNbitsStatus;
  lnd?: LNDStatus;
  bitcoin?: BitcoinStatus;
  overall: "ok" | "degraded" | "down";
  issues: string[];
}

interface LNbitsStatus {
  reachable: boolean;
  url: string;
  walletBalance?: number;
  error?: string;
}

interface LNDStatus {
  synced: boolean;
  blockHeight: number;
  activeChannels: number;
  walletBalance: number;
  channelBalance: number;
}

interface BitcoinStatus {
  blocks: number;
  headers: number;
  syncPct: number;
  pruned: boolean;
  peers: number;
}

async function probeLNbits(): Promise<LNbitsStatus> {
  const url = process.env.LNBITS_URL ?? "";
  const key = process.env.LNBITS_API_KEY ?? "";

  if (!url || !key) {
    return { reachable: false, url: url || "(not configured)", error: "LNBITS_URL or LNBITS_API_KEY not set" };
  }

  try {
    const res = await fetch(`${url}/api/v1/wallet`, {
      headers: { "X-Api-Key": key },
      signal: AbortSignal.timeout(5000),
    });
    if (!res.ok) {
      return { reachable: false, url, error: `HTTP ${res.status}` };
    }
    const data = (await res.json()) as { balance: number };
    return { reachable: true, url, walletBalance: Math.round((data.balance ?? 0) / 1000) };
  } catch (e) {
    return { reachable: false, url, error: String(e) };
  }
}

async function probeLNbitsNodeInfo(): Promise<{ lnd?: LNDStatus; bitcoin?: BitcoinStatus }> {
  const url = process.env.LNBITS_URL ?? "";
  const key = process.env.LNBITS_API_KEY ?? "";
  if (!url || !key) return {};

  try {
    const res = await fetch(`${url}/api/v1/extension/lndhub/api/v1/getinfo`, {
      headers: { "X-Api-Key": key },
      signal: AbortSignal.timeout(5000),
    });
    if (!res.ok) return {};
    const data = (await res.json()) as Record<string, unknown>;

    const lnd: LNDStatus = {
      synced: Boolean(data["synced_to_chain"]),
      blockHeight: Number(data["block_height"] ?? 0),
      activeChannels: Number(data["num_active_channels"] ?? 0),
      walletBalance: 0,
      channelBalance: 0,
    };
    return { lnd };
  } catch {
    return {};
  }
}

router.get("/api/admin/node-status", async (_req, res) => {
  const issues: string[] = [];

  const [lnbits, nodeInfo] = await Promise.all([
    probeLNbits(),
    probeLNbitsNodeInfo(),
  ]);

  if (!lnbits.reachable) {
    issues.push(`LNbits unreachable: ${lnbits.error ?? "unknown"}`);
  }
  if (nodeInfo.lnd && !nodeInfo.lnd.synced) {
    issues.push("LND not yet synced to chain tip");
  }

  const overall: NodeStatus["overall"] =
    !lnbits.reachable ? "down" :
    issues.length > 0  ? "degraded" :
    "ok";

  const status: NodeStatus = {
    timestamp: new Date().toISOString(),
    lnbits,
    ...nodeInfo,
    overall,
    issues,
  };

  res.json(status);
});

router.get("/api/admin/node-status/html", async (_req, res) => {
  const issues: string[] = [];
  const [lnbits, nodeInfo] = await Promise.all([probeLNbits(), probeLNbitsNodeInfo()]);

  if (!lnbits.reachable) issues.push(`LNbits unreachable: ${lnbits.error ?? "unknown"}`);
  if (nodeInfo.lnd && !nodeInfo.lnd.synced) issues.push("LND not yet synced");

  const overall = !lnbits.reachable ? "down" : issues.length > 0 ? "degraded" : "ok";
  const color = overall === "ok" ? "#22c55e" : overall === "degraded" ? "#f59e0b" : "#ef4444";

  res.setHeader("Content-Type", "text/html");
  res.send(`<!DOCTYPE html><html lang="en">
<head><meta charset="UTF-8"><title>Timmy — Node Status</title>
<style>
  body { font-family: monospace; background:#0f172a; color:#e2e8f0; padding:2rem; }
  h1 { color:#f8fafc; } .badge { padding:4px 12px; border-radius:4px; font-weight:bold; }
  table { border-collapse:collapse; margin-top:1rem; }
  td,th { padding:6px 16px; border-bottom:1px solid #1e293b; text-align:left; }
  th { color:#94a3b8; } .ok{color:#22c55e} .warn{color:#f59e0b} .bad{color:#ef4444}
  pre { background:#1e293b; padding:1rem; border-radius:6px; overflow:auto; }
</style></head><body>
<h1>⚡ Timmy Node Status</h1>
<p>Overall: <span class="badge" style="background:${color}20;color:${color}">${overall.toUpperCase()}</span></p>

<h2>LNbits</h2>
<table>
  <tr><th>URL</th><td>${lnbits.url}</td></tr>
  <tr><th>Reachable</th><td class="${lnbits.reachable ? "ok" : "bad"}">${lnbits.reachable ? "✓ yes" : "✗ no"}</td></tr>
  ${lnbits.walletBalance !== undefined ? `<tr><th>Wallet Balance</th><td>${lnbits.walletBalance} sats</td></tr>` : ""}
  ${lnbits.error ? `<tr><th>Error</th><td class="bad">${lnbits.error}</td></tr>` : ""}
</table>

${nodeInfo.lnd ? `<h2>LND</h2>
<table>
  <tr><th>Synced</th><td class="${nodeInfo.lnd.synced ? "ok" : "warn"}">${nodeInfo.lnd.synced ? "✓ yes" : "⏳ syncing"}</td></tr>
  <tr><th>Block Height</th><td>${nodeInfo.lnd.blockHeight.toLocaleString()}</td></tr>
  <tr><th>Active Channels</th><td>${nodeInfo.lnd.activeChannels}</td></tr>
</table>` : ""}

${issues.length > 0 ? `<h2>Issues</h2><ul>${issues.map(i => `<li class="bad">${i}</li>`).join("")}</ul>` : ""}

<p style="color:#475569;margin-top:2rem;font-size:0.8rem">
  Refreshed at ${new Date().toISOString()} &nbsp;·&nbsp;
  <a href="/api/admin/node-status" style="color:#60a5fa">JSON</a> &nbsp;·&nbsp;
  <a href="/api/ui" style="color:#60a5fa">Timmy UI</a>
</p>
</body></html>`);
});

export default router;
