#!/usr/bin/env node
import { ComponentRegistry } from "./registry.js";
import { handleDiscover } from "./tools/discover.js";
import { handleInspect } from "./tools/inspect.js";
import { handleAdd } from "./tools/add.js";
import { mkdir, writeFile, copyFile, access } from "node:fs/promises";
import { dirname, resolve, join } from "node:path";
import { fileURLToPath } from "node:url";

interface ParsedCommand {
  command: string;
  args: Record<string, string>;
}

const VALID_COMMANDS: string[] = ["init", "add", "inspect", "list", "discover", "help"];
const COMPONENT_COMMANDS: string[] = ["add", "inspect", "discover"];

function isString(v: unknown): v is string {
  return typeof v === "string";
}

/** Parses CLI arguments into command + args. */
export function parseCommand(argv: string[]): ParsedCommand {
  if (argv.length === 0) return { command: "help", args: {} };
  const rawCommand = argv[0];
  const command = isString(rawCommand) ? rawCommand : "help";
  const args: Record<string, string> = {};
  if (COMPONENT_COMMANDS.includes(command) && argv.length > 1) {
    const second = argv[1];
    if (isString(second)) args["component"] = second;
  }
  for (let i = 1; i < argv.length; i++) {
    if (argv[i] === "--dir") {
      const next = argv[i + 1];
      if (isString(next)) { args["dir"] = next; i++; }
    }
  }
  if (!VALID_COMMANDS.includes(command)) {
    return { command: "help", args: {} };
  }
  return { command, args };
}

/** Writes a line to stdout. */
function out(line: string): void {
  process.stdout.write(line + "\n");
}

/** Writes an error line to stderr and exits 1. */
function fail(message: string): never {
  process.stderr.write("Error: " + message + "\n");
  process.exit(1);
}

/** Attempts to copy a file; throws with a descriptive message if source is missing. */
async function tryCopyAsset(src: string, dest: string, label: string): Promise<void> {
  try {
    await access(src);
  } catch (err) {
    throw new Error(`${label} not found at ${src}: ${String(err)}`);
  }
  await copyFile(src, dest);
  out(`  Copied ${label} → ${dest}`);
}

/** Handles the init command. */
async function cmdInit(args: Record<string, string>): Promise<void> {
  const targetDir = args["dir"] ?? "src/styles";
  await mkdir(targetDir, { recursive: true });
  const registryBase = resolve(dirname(fileURLToPath(import.meta.url)), "../../registry/src");
  await tryCopyAsset(join(registryBase, "tokens.css"), join(targetDir, "tokens.css"), "tokens.css");
  await tryCopyAsset(join(registryBase, "utils.ts"), join(targetDir, "utils.ts"), "utils.ts");
  out("Done. Run `pettyui list` to see available components.");
}

/** Handles the list command. */
function cmdList(registry: ComponentRegistry): void {
  const components = registry.listComponents();
  out(`PettyUI components (${components.length} total):\n`);
  for (const comp of components) {
    const styled = comp.hasStyledVersion ? " [styled]" : "";
    out(`  ${comp.meta.name.padEnd(20)} ${comp.meta.description}${styled}`);
  }
}

/** Handles the inspect command. */
function cmdInspect(registry: ComponentRegistry, args: Record<string, string>): void {
  const name = args["component"] ?? fail("inspect requires a component name\nUsage: pettyui inspect <name>");
  const result = handleInspect(registry, { component: name });
  if (!result) fail(`Component "${name}" not found. Run \`pettyui list\` to see available components.`);
  out(JSON.stringify(result, null, 2));
}

/** Handles the discover command. */
function cmdDiscover(registry: ComponentRegistry, args: Record<string, string>): void {
  const intent = args["component"] ?? fail("discover requires a description\nUsage: pettyui discover <intent>");
  const results = handleDiscover(registry, { intent });
  if (results.length === 0) { out(`No components found for: "${intent}"`); return; }
  out(`Found ${results.length} component(s) matching "${intent}":\n`);
  for (const r of results) {
    const styled = r.hasStyledVersion ? " [styled]" : "";
    out(`  ${r.name.padEnd(20)} score=${r.score}  ${r.description}${styled}`);
  }
}

/** Handles the add command. */
async function cmdAdd(registry: ComponentRegistry, args: Record<string, string>): Promise<void> {
  const name = args["component"] ?? fail("add requires a component name\nUsage: pettyui add <name>");
  const targetDir = args["dir"] ?? "src/components/ui";
  const result = handleAdd(registry, { component: name, targetDir });
  if (!result) {
    fail(
      `Component "${name}" not found or has no styled version.\n` +
      `Run \`pettyui list\` to see components. Styled components are marked [styled].`,
    );
  }
  await mkdir(targetDir, { recursive: true });
  for (const file of result.files) {
    await writeFile(file.path, file.content, "utf-8");
    out(`  Created ${file.path}`);
  }
  if (result.dependencies.length > 0) {
    out("\nAdd this dependency to your project:");
    for (const dep of result.dependencies) { out(`  ${dep}`); }
  }
  out("Done.");
}

/** Prints CLI help. */
function cmdHelp(): void {
  out([
    "PettyUI CLI — AI-first SolidJS component library",
    "",
    "Usage:",
    "  pettyui <command> [options]",
    "",
    "Commands:",
    "  init                Copy tokens.css and utils.ts to your project",
    "  list                List all available components",
    "  inspect <name>      Show full schema and usage for a component",
    "  discover <intent>   Search for components by describing what you need",
    "  add <name>          Add a styled component to your project",
    "  help                Show this help message",
    "",
    "Options:",
    "  --dir <path>        Target directory (default: src/components/ui)",
    "",
    "Examples:",
    "  pettyui list",
    "  pettyui inspect dialog",
    '  pettyui discover "modal with confirmation"',
    "  pettyui add dialog --dir src/ui",
    "  pettyui init --dir src/styles",
  ].join("\n"));
}

/** Runs the CLI with the given argument vector (process.argv.slice(2)). */
export async function runCli(argv: string[]): Promise<void> {
  const { command, args } = parseCommand(argv);
  const registry = new ComponentRegistry();

  switch (command) {
    case "init":     return cmdInit(args);
    case "list":     return cmdList(registry);
    case "inspect":  return cmdInspect(registry, args);
    case "discover": return cmdDiscover(registry, args);
    case "add":      return cmdAdd(registry, args);
    default:         return cmdHelp();
  }
}

runCli(process.argv.slice(2)).catch((err: unknown) => {
  process.stderr.write("Fatal: " + (err instanceof Error ? err.message : string(err)) + "\n");
  process.exit(1);
});
