// cli/commands/doctor.js
// clauth doctor — check all prerequisites for a healthy installation
// Checks: Node version, cloudflared, network, Supabase connectivity, config, service status

import os from "os";
import fs from "fs";
import path from "path";
import { execSync } from "child_process";
import { fileURLToPath } from "url";
import chalk from "chalk";
import ora from "ora";
import Conf from "conf";
import { getConfOptions } from "../conf-path.js";
import { getMachineHash } from "../fingerprint.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf8"));

const CHECK = chalk.green("\u2713");
const CROSS = chalk.red("\u2717");
const WARN  = chalk.yellow("!");

/**
 * Run a shell command and return stdout, or null on failure.
 */
function execSafe(cmd, timeoutMs = 10000) {
  try {
    return execSync(cmd, {
      encoding: "utf8",
      timeout: timeoutMs,
      stdio: ["pipe", "pipe", "pipe"],
    }).trim();
  } catch {
    return null;
  }
}

export async function runDoctor() {
  const results = [];
  const platform = os.platform();

  console.log(chalk.cyan(`\n  clauth doctor v${pkg.version}\n`));
  console.log(chalk.gray(`  Platform: ${platform} (${os.arch()})`));
  console.log(chalk.gray(`  Node:     ${process.version}`));
  console.log();

  // ── 1. Node version ──────────────────────────────────────
  {
    const major = parseInt(process.versions.node.split(".")[0], 10);
    if (major >= 18) {
      results.push(true);
      console.log(`  ${CHECK} Node.js ${process.versions.node} (>= 18 required)`);
    } else {
      results.push(false);
      console.log(`  ${CROSS} Node.js ${process.versions.node} — version 18+ required`);
      console.log(chalk.gray("    Fix: Install Node.js 18+ from https://nodejs.org/"));
    }
  }

  // ── 2. cloudflared ───────────────────────────────────────
  {
    // Check common Windows paths if not on PATH
    let cfVersion = execSafe("cloudflared --version");
    if (!cfVersion && platform === "win32") {
      const winPaths = [
        path.join(process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)", "cloudflared", "cloudflared.exe"),
        path.join(process.env.ProgramFiles || "C:\\Program Files", "cloudflared", "cloudflared.exe"),
        path.join(os.homedir(), "scoop", "shims", "cloudflared.exe"),
        "C:\\ProgramData\\chocolatey\\bin\\cloudflared.exe",
      ];
      for (const p of winPaths) {
        try { if (fs.statSync(p).isFile()) { cfVersion = execSafe(`"${p}" --version`); break; } } catch {}
      }
    }
    if (cfVersion) {
      results.push(true);
      console.log(`  ${CHECK} cloudflared installed (${cfVersion.split("\n")[0]})`);
    } else {
      results.push(false);
      console.log(`  ${CROSS} cloudflared not found`);
      if (platform === "win32") {
        console.log(chalk.gray("    Fix: winget install Cloudflare.cloudflared"));
        console.log(chalk.gray("     or: choco install cloudflared -y"));
      } else if (platform === "darwin") {
        console.log(chalk.gray("    Fix: brew install cloudflared"));
      } else {
        console.log(chalk.gray("    Fix: curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared"));
        console.log(chalk.gray("     or: snap install cloudflared"));
      }
    }
  }

  // ── 3. Config exists ─────────────────────────────────────
  {
    const config = new Conf(getConfOptions());
    const supaUrl = config.get("supabase_url");
    const anonKey = config.get("supabase_anon_key");

    if (supaUrl && anonKey) {
      results.push(true);
      console.log(`  ${CHECK} Config found (Supabase URL + anon key saved)`);
      console.log(chalk.gray(`    URL: ${supaUrl}`));
    } else if (supaUrl && !anonKey) {
      results.push(false);
      console.log(`  ${CROSS} Config incomplete — Supabase anon key missing`);
      console.log(chalk.gray("    Fix: Run 'clauth install' to complete setup"));
    } else {
      results.push(false);
      console.log(`  ${CROSS} Config not found — clauth not installed`);
      console.log(chalk.gray("    Fix: Run 'clauth install' to set up your vault"));
    }
  }

  // ── 4. Network: can reach Supabase ───────────────────────
  {
    const config = new Conf(getConfOptions());
    const supaUrl = config.get("supabase_url");

    if (supaUrl) {
      try {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 8000);
        const res = await fetch(`${supaUrl}/rest/v1/`, {
          method: "HEAD",
          signal: controller.signal,
          headers: { apikey: config.get("supabase_anon_key") || "" },
        });
        clearTimeout(timeout);
        if (res.ok || res.status === 401 || res.status === 400) {
          results.push(true);
          console.log(`  ${CHECK} Network — Supabase reachable (HTTP ${res.status})`);
        } else {
          results.push(false);
          console.log(`  ${CROSS} Network — Supabase returned HTTP ${res.status}`);
        }
      } catch (err) {
        results.push(false);
        console.log(`  ${CROSS} Network — cannot reach Supabase: ${err.message}`);
        console.log(chalk.gray("    Check your internet connection"));
      }
    } else {
      results.push(null);
      console.log(`  ${WARN} Network — skipped (no Supabase URL configured)`);
    }
  }

  // ── 5. Machine fingerprint ───────────────────────────────
  {
    try {
      const hash = getMachineHash();
      results.push(true);
      console.log(`  ${CHECK} Machine fingerprint: ${hash.slice(0, 16)}...`);
    } catch (err) {
      results.push(false);
      console.log(`  ${CROSS} Machine fingerprint failed: ${err.message}`);
      console.log(chalk.gray("    Fix: Run 'clauth setup' to register this machine"));
    }
  }

  // ── 6. Daemon status ─────────────────────────────────────
  {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 3000);
      const res = await fetch("http://127.0.0.1:52437/ping", { signal: controller.signal });
      clearTimeout(timeout);
      if (res.ok) {
        const data = await res.json();
        results.push(true);
        const locked = data.locked ? chalk.yellow(" (locked)") : chalk.green(" (unlocked)");
        console.log(`  ${CHECK} Daemon running on port ${data.port || 52437}${locked}`);
      } else {
        results.push(false);
        console.log(`  ${CROSS} Daemon responded with HTTP ${res.status}`);
      }
    } catch {
      results.push(false);
      console.log(`  ${CROSS} Daemon not running`);
      console.log(chalk.gray("    Fix: clauth serve start"));
    }
  }

  // ── 7. Autostart / service registration ──────────────────
  {
    if (platform === "win32") {
      const taskOutput = execSafe('schtasks /query /tn "ClauthAutostart" 2>&1');
      if (taskOutput && !taskOutput.includes("ERROR")) {
        results.push(true);
        console.log(`  ${CHECK} Windows Scheduled Task registered (ClauthAutostart)`);
      } else {
        results.push(null);
        console.log(`  ${WARN} Windows Scheduled Task not registered`);
        console.log(chalk.gray("    Optional: clauth serve install (auto-start on login)"));
      }
    } else if (platform === "darwin") {
      const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.lifeai.clauth.plist");
      if (fs.existsSync(plistPath)) {
        results.push(true);
        console.log(`  ${CHECK} macOS LaunchAgent registered`);
      } else {
        results.push(null);
        console.log(`  ${WARN} macOS LaunchAgent not registered`);
        console.log(chalk.gray("    Optional: clauth serve install (auto-start on login)"));
      }
    } else {
      const serviceFile = path.join(os.homedir(), ".config", "systemd", "user", "clauth.service");
      if (fs.existsSync(serviceFile)) {
        results.push(true);
        console.log(`  ${CHECK} systemd user service registered`);
      } else {
        results.push(null);
        console.log(`  ${WARN} systemd user service not registered`);
        console.log(chalk.gray("    Optional: clauth serve install (auto-start on login)"));
      }
    }
  }

  // ── 8. Tunnel configuration ──────────────────────────────
  {
    const config = new Conf(getConfOptions());
    const tunnelHostname = config.get("tunnel_hostname");
    if (tunnelHostname) {
      results.push(true);
      console.log(`  ${CHECK} Tunnel configured: ${tunnelHostname}`);
    } else {
      results.push(null);
      console.log(`  ${WARN} Tunnel not configured (optional for remote MCP access)`);
      console.log(chalk.gray("    Optional: clauth serve install --tunnel <hostname>"));
    }
  }

  // ── Summary ──────────────────────────────────────────────
  console.log();
  const failed = results.filter(r => r === false).length;
  const warnings = results.filter(r => r === null).length;
  const passed = results.filter(r => r === true).length;

  if (failed === 0) {
    console.log(chalk.green(`  All ${passed} checks passed.`));
    if (warnings > 0) {
      console.log(chalk.yellow(`  ${warnings} optional recommendation${warnings > 1 ? "s" : ""}.`));
    }
  } else {
    console.log(chalk.red(`  ${failed} issue${failed > 1 ? "s" : ""} found.`));
    if (passed > 0) console.log(chalk.green(`  ${passed} check${passed > 1 ? "s" : ""} passed.`));
    if (warnings > 0) console.log(chalk.yellow(`  ${warnings} optional.`));
    console.log(chalk.gray("\n  Run the suggested fix commands above to resolve issues."));
  }
  console.log();
}

/**
 * Install cloudflared on the current platform.
 * Exported so other commands (e.g., serve install) can offer auto-install.
 */
export async function installCloudflared() {
  const platform = os.platform();
  const spinner = ora("Installing cloudflared...").start();

  try {
    if (platform === "win32") {
      // Try winget first
      try {
        execSync(
          "winget install Cloudflare.cloudflared --accept-source-agreements --accept-package-agreements",
          { stdio: "inherit", timeout: 120000 }
        );
        spinner.succeed(chalk.green("cloudflared installed via winget"));
        return true;
      } catch {
        spinner.text = "winget failed, trying chocolatey...";
      }

      // Try chocolatey
      try {
        execSync("choco install cloudflared -y", { stdio: "inherit", timeout: 120000 });
        spinner.succeed(chalk.green("cloudflared installed via chocolatey"));
        return true;
      } catch {
        spinner.fail(chalk.red("Could not install cloudflared automatically"));
        console.log(chalk.gray("  Download manually: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/"));
        return false;
      }
    } else if (platform === "darwin") {
      execSync("brew install cloudflared", { stdio: "inherit", timeout: 120000 });
      spinner.succeed(chalk.green("cloudflared installed via Homebrew"));
      return true;
    } else {
      // Linux
      execSync(
        "curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared",
        { stdio: "inherit", timeout: 120000 }
      );
      spinner.succeed(chalk.green("cloudflared installed"));
      return true;
    }
  } catch (err) {
    spinner.fail(chalk.red(`cloudflared installation failed: ${err.message}`));
    console.log(chalk.gray("  Install manually: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/"));
    return false;
  }
}
