#!/usr/bin/env node
"use strict";

const { spawn } = require("child_process");
const fs = require("fs");
const net = require("net");
const path = require("path");

function readPkg() {
  return JSON.parse(
    fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
  );
}

function findFreePort(start) {
  return new Promise((resolve) => {
    let port = start;
    const tryPort = () => {
      const server = net.createServer();
      server.once("error", () => { port += 1; tryPort(); });
      server.once("listening", () => { server.close(() => resolve(port)); });
      server.listen(port, "127.0.0.1");
    };
    tryPort();
  });
}

function nextBin() {
  try {
    return require.resolve("next/dist/bin/next");
  } catch {
    const local = path.join(__dirname, "..", "node_modules", ".bin", "next");
    if (fs.existsSync(local)) return local;
    return "next";
  }
}

function packageDir() {
  return path.resolve(__dirname, "..");
}

function readForgeConfig(targetPath) {
  const configPath = path.join(targetPath, ".forge", "config.json");
  if (!fs.existsSync(configPath)) return null;
  try {
    return JSON.parse(fs.readFileSync(configPath, "utf-8"));
  } catch {
    return null;
  }
}

async function cmdDev(argv) {
  let port = 3002;
  let target = process.cwd();

  for (let i = 0; i < argv.length; i++) {
    if ((argv[i] === "--port" || argv[i] === "-p") && argv[i + 1]) {
      port = parseInt(argv[++i], 10);
    } else if (argv[i] === "--target" && argv[i + 1]) {
      target = path.resolve(argv[++i]);
    } else if (/^--port=/.test(argv[i])) {
      port = parseInt(argv[i].split("=")[1], 10);
    } else if (/^--target=/.test(argv[i])) {
      target = path.resolve(argv[i].split("=")[1]);
    }
  }

  port = await findFreePort(port);

  const banner = [
    "",
    "  \x1b[33m⚒ forge\x1b[0m\x1b[38;5;208m0x2B\x1b[0m  →  \x1b[1mhttp://localhost:" + port + "\x1b[0m",
    `  \x1b[2mTarget:\x1b[0m \x1b[33m${target}\x1b[0m`,
    "  \x1b[2mForge the message from the code.\x1b[0m",
    "",
  ].join("\n");
  process.stdout.write(banner);

  // Warn if no API key configured
  const config = readForgeConfig(target);
  if (!config || !config.apiKey) {
    process.stdout.write(
      `  \x1b[33m⚠\x1b[0m  No API key configured → http://localhost:${port}/settings\n\n`
    );
  }

  const env = {
    ...process.env,
    FORGE_TARGET: target,
    FORGE_PORT: String(port),
    PORT: String(port),
  };

  const pkgDir = packageDir();
  const isInstalledPkg = pkgDir.includes("node_modules");
  const standaloneServer = path.join(pkgDir, ".next", "standalone", "server.js");

  let child;
  if (isInstalledPkg && fs.existsSync(standaloneServer)) {
    child = spawn(process.execPath, [standaloneServer], {
      cwd: pkgDir,
      env: { ...env, HOSTNAME: "0.0.0.0" },
      stdio: "inherit",
      shell: false,
    });
  } else {
    const next = nextBin();
    const nextCmd = isInstalledPkg ? "start" : "dev";
    child = spawn(next, [nextCmd, "-p", String(port)], {
      cwd: pkgDir,
      env,
      stdio: "inherit",
      shell: process.platform === "win32",
    });
  }

  const shutdown = (sig) => { child.kill(sig); process.exit(0); };
  process.on("SIGINT", () => shutdown("SIGINT"));
  process.on("SIGTERM", () => shutdown("SIGTERM"));
  child.on("exit", (code) => process.exit(code ?? 0));
}

function cmdInit() {
  const cwd = process.cwd();
  const forgeDir = path.join(cwd, ".forge");
  const configPath = path.join(forgeDir, "config.json");

  // Idempotent: don't overwrite existing config
  if (fs.existsSync(configPath)) {
    console.log(`  \x1b[2m→\x1b[0m Config already exists at ${configPath} — not overwritten.`);
    return;
  }

  if (!fs.existsSync(forgeDir)) fs.mkdirSync(forgeDir, { recursive: true });

  const config = {
    version: 1,
    targetPath: cwd,
    port: 3002,
  };

  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", { encoding: "utf-8", mode: 0o600 });

  const hostPkgPath = path.join(cwd, "package.json");
  if (fs.existsSync(hostPkgPath)) {
    let hostPkg;
    try { hostPkg = JSON.parse(fs.readFileSync(hostPkgPath, "utf-8")); } catch { hostPkg = null; }
    if (hostPkg && !hostPkg.scripts?.forge) {
      hostPkg.scripts = hostPkg.scripts ?? {};
      hostPkg.scripts.forge = "forge dev";
      fs.writeFileSync(hostPkgPath, JSON.stringify(hostPkg, null, 2) + "\n", "utf-8");
      console.log('  \x1b[2m+\x1b[0m Added "forge" script to package.json');
    }
  }

  console.log(`\n     \x1b[33m⚒ forge\x1b[0m\x1b[38;5;208m0x2B\x1b[0m initialized`);
  console.log(`\n  Config → ${configPath}`);
  console.log(`\n  Setup checklist:`);
  console.log(`    1. Start dashboard: npm run forge (or npx forge dev)`);
  console.log(`    2. Add API key: http://localhost:3002/settings`);
  console.log(`    3. Scan codebase: prism scan (install prism0x2A first)`);
  console.log(`    4. Generate: open http://localhost:3002/forge/from-prism`);
  console.log(`\n  Run \x1b[1mforge doctor\x1b[0m to check your setup at any time.\n`);
}

function cmdDoctor(argv) {
  let target = process.cwd();

  for (let i = 0; i < argv.length; i++) {
    if (argv[i] === "--target" && argv[i + 1]) {
      target = path.resolve(argv[++i]);
    } else if (/^--target=/.test(argv[i])) {
      target = path.resolve(argv[i].split("=")[1]);
    }
  }

  const G = "\x1b[32m";
  const R = "\x1b[31m";
  const RESET = "\x1b[0m";
  const DIM = "\x1b[2m";

  console.log(`\n  \x1b[33m⚒ forge\x1b[0m\x1b[38;5;208m0x2B\x1b[0m doctor  →  ${target}\n`);

  let issues = 0;

  // Check 1: .forge/config.json
  const configPath = path.join(target, ".forge", "config.json");
  let config = null;
  if (!fs.existsSync(configPath)) {
    issues++;
    console.log(`  ${R}✗${RESET}  Config         No .forge/config.json found`);
    console.log(`                    ${DIM}→ Run: forge init${RESET}`);
  } else {
    console.log(`  ${G}✓${RESET}  Config         .forge/config.json present`);
    try { config = JSON.parse(fs.readFileSync(configPath, "utf-8")); } catch { config = null; }
  }

  // Check 2: API key
  if (config && config.apiKey && config.apiKey.trim() !== "") {
    console.log(`  ${G}✓${RESET}  API key        Configured`);
  } else if (config) {
    issues++;
    console.log(`  ${R}✗${RESET}  API key        Not set`);
    console.log(`                    ${DIM}→ Add at: http://localhost:3002/settings${RESET}`);
  } else {
    // config missing entirely — already flagged above, skip API key row
  }

  // Check 3: .prism/blueprint/snapshot.json
  const snapshotPath = path.join(target, ".prism", "blueprint", "snapshot.json");
  if (fs.existsSync(snapshotPath)) {
    console.log(`  ${G}✓${RESET}  Blueprint      .prism/blueprint/snapshot.json present`);
  } else {
    issues++;
    console.log(`  ${R}✗${RESET}  Blueprint      No .prism/blueprint/snapshot.json found`);
    console.log(`                    ${DIM}→ Run: prism scan${RESET}`);
  }

  console.log("");
  if (issues === 0) {
    console.log(`  ${G}Setup: complete — all checks passed.${RESET}\n`);
  } else {
    console.log(`  ${R}Setup: incomplete — ${issues} issue(s)${RESET}\n`);
  }
}

function cmdVersion() {
  console.log(readPkg().version);
}

function cmdHelp() {
  const pkg = readPkg();
  console.log(`
  \x1b[33m⚒ forge\x1b[0m\x1b[38;5;208m0x2B\x1b[0m v${pkg.version} — Forge the message from the code.

  \x1b[1mUsage:\x1b[0m
    forge <command> [options]

  \x1b[1mCommands:\x1b[0m
    dev [--port N] [--target PATH]
        Start the forge0x2B dashboard.
        --port   Port to listen on (default: 3002, auto-increments if busy)
        --target Directory to use as target (default: cwd)

    init
        Initialize forge0x2B in the current project.
        Creates .forge/config.json and adds "forge" script to package.json.

    doctor [--target PATH]
        Diagnose your forge0x2B setup. Checks API key, prism blueprint.

  \x1b[1mOptions:\x1b[0m
    --version, -v   Print version
    --help, -h      Show this help

  \x1b[1mExamples:\x1b[0m
    npx forge dev
    npx forge dev --port 4000
    npx forge init
    npx forge doctor
`);
}

const args = process.argv.slice(2);
const cmd = args[0];

if (!cmd || cmd === "--help" || cmd === "-h") {
  cmdHelp();
} else if (cmd === "--version" || cmd === "-v") {
  cmdVersion();
} else if (cmd === "dev") {
  cmdDev(args.slice(1)).catch((err) => {
    console.error("forge dev error:", err.message);
    process.exit(1);
  });
} else if (cmd === "init") {
  cmdInit();
} else if (cmd === "doctor") {
  cmdDoctor(args.slice(1));
} else {
  console.error(`\n  Unknown command: ${cmd}\n  Run \`forge --help\` for usage.\n`);
  process.exit(1);
}
