#!/usr/bin/env node
// career-ops scaffolder — one-command install.
// Clones the repo at the latest release tag and installs dependencies.
// It deliberately does NOT create cv.md / config/profile.yml / portals.yml:
// the agent runs a conversational onboarding on first launch (see AGENTS.md
// "First Run — Onboarding"), which is triggered precisely by those files
// being absent. Pre-creating them from the examples would suppress that
// onboarding and leave the user with placeholder data.
import { execFileSync } from "node:child_process";
import { existsSync, readdirSync } from "node:fs";
import { join, delimiter } from "node:path";
import { ensureSkillEntrypoints } from "./skill-entrypoints.mjs";

const REPO = "https://github.com/santifer/career-ops.git";
const LATEST_RELEASE = "https://api.github.com/repos/santifer/career-ops/releases/latest";
const NPM = process.platform === "win32" ? "npm.cmd" : "npm";

// career-ops is AI-agnostic: every one of these CLIs reads AGENTS.md and works
// out of the box. We only detect them to tailor the final message — we never
// install, configure, or remove anything per-CLI.
const SUPPORTED_CLIS = [
  { name: "Claude Code", cmd: "claude" },
  { name: "Gemini CLI", cmd: "gemini" },
  { name: "Codex", cmd: "codex" },
  { name: "Qwen Code", cmd: "qwen" },
  { name: "OpenCode", cmd: "opencode" },
  { name: "GitHub Copilot CLI", cmd: "copilot" },
  { name: "Antigravity CLI", cmd: "agy" },
  { name: "Grok Build CLI", cmd: "grok" },
];

const USAGE = `career-ops — set up an AI job search workspace.

Usage:
  npx career-ops init [folder]    Create a new workspace (default: ./career-ops)

After setup, open your AI coding tool inside the folder and paste a job offer.
Docs: https://github.com/santifer/career-ops`;

function die(msg) {
  console.error(`\n✗ ${msg}\n`);
  process.exit(1);
}

function has(cmd, arg = "--version") {
  try {
    execFileSync(cmd, [arg], { stdio: "ignore" });
    return true;
  } catch {
    return false;
  }
}

// Is `cmd` resolvable on PATH? Manual lookup so it works cross-platform
// without spawning which/where (and without any dependency).
function onPath(cmd) {
  const exts = process.platform === "win32" ? (process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";") : [""];
  for (const dir of (process.env.PATH || "").split(delimiter)) {
    if (!dir) continue;
    for (const ext of exts) {
      try {
        if (existsSync(join(dir, cmd + ext))) return true;
      } catch {}
    }
  }
  return false;
}

function detectClis() {
  return SUPPORTED_CLIS.filter((c) => onPath(c.cmd));
}

async function latestTag() {
  try {
    const res = await fetch(LATEST_RELEASE, {
      headers: { "User-Agent": "career-ops-cli", Accept: "application/vnd.github+json" },
    });
    if (!res.ok) return null;
    const data = await res.json();
    return data.tag_name || null;
  } catch {
    return null;
  }
}

async function main() {
  const [cmd, dirArg] = process.argv.slice(2);

  if (!cmd || cmd === "-h" || cmd === "--help") {
    console.log(USAGE);
    process.exit(cmd ? 0 : 1);
  }
  if (cmd !== "init") die(`Unknown command "${cmd}".\n${USAGE}`);

  const target = dirArg || "career-ops";
  if (existsSync(target) && readdirSync(target).length > 0) {
    die(`Target folder "${target}" already exists and is not empty. Pick another name.`);
  }
  if (!has("git")) die("git is required but was not found on PATH. Install git and try again.");

  // Pretty path for messages: "./career-ops" for relative, as-is for absolute.
  const isAbsolute = target.startsWith("/") || /^[A-Za-z]:/.test(target);
  const display = isAbsolute ? target : `./${target}`;

  // 1. Clone at the latest stable release (fall back to the default branch).
  const tag = await latestTag();
  console.log(`\n→ Cloning career-ops${tag ? ` @ ${tag}` : ""} into ${display} ...`);
  const cloneArgs = ["clone", "--depth=1"];
  if (tag) cloneArgs.push("--branch", tag);
  cloneArgs.push(REPO, target);
  try {
    execFileSync("git", cloneArgs, { stdio: "inherit" });
  } catch {
    die("git clone failed. Check your network connection and try again.");
  }

  // 2. Install dependencies.
  console.log("\n→ Installing dependencies (npm install) ...");
  try {
    execFileSync(NPM, ["install"], { cwd: target, stdio: "inherit" });
  } catch {
    console.warn('\n! npm install failed — you can re-run it manually later with "npm install".');
  }

  // 2b. Bootstrap CLI skill entrypoints (covers CLIs added after the cloned release).
  const bootstrapped = ensureSkillEntrypoints(target);
  if (bootstrapped.length > 0) {
    console.log(`\n→ Bootstrapped ${bootstrapped.length} CLI skill entrypoint(s) for this workspace`);
  }

  // 3. Next steps. We do NOT scaffold cv.md / profile.yml / portals.yml here:
  // their absence is what triggers the agent's conversational onboarding on
  // first launch, which sets them up far better than copying placeholders.
  console.log(`\n✓ career-ops is ready in ${display}\n`);
  console.log("Next steps:");
  console.log(`  1. cd ${target}`);

  // Tailor the "open your AI tool" line to whatever CLI is installed.
  const detected = detectClis();
  if (detected.length === 1) {
    console.log(`  2. Open your workspace:  ${detected[0].cmd}   (${detected[0].name} detected)`);
  } else if (detected.length > 1) {
    console.log(`  2. Open your workspace with any of:  ${detected.map((c) => c.cmd).join(", ")}   (detected)`);
  } else {
    console.log(`  2. Open your AI coding tool here, e.g.:  ${SUPPORTED_CLIS.map((c) => c.cmd).join(", ")}`);
  }

  console.log("\nOn first launch it walks you through setup — your CV, profile and target");
  console.log("roles — just by chatting. Nothing to configure by hand.");
  console.log("\ncareer-ops is AI-agnostic — Claude Code, Codex, Qwen, OpenCode, Copilot, Antigravity and Grok all work.");
  console.log("\nOptional (for PDF generation):");
  console.log("  npx playwright install chromium\n");
}

main().catch((err) => die(err?.message || String(err)));
