#!/usr/bin/env node
import { chdir } from "node:process";
import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { GrokBridge } from "./grok/GrokBridge.js";
import { defaultOutputPath, formatReport, runPipeline } from "./pipeline.js";

/**
 * `psx-harness` — photo to rigged, animated GLB.
 *
 *   pnpm harness --describe "a lone survivor in a canvas jacket" --name Survivor
 *   pnpm harness --image tools/fixtures/survivor-reference.jpg --name Survivor
 *
 * Paths are resolved from the monorepo root so the same flags work whether
 * you invoke via the root script, `pnpm --filter @psx/tools harness`, or
 * `tsx tools/src/cli.ts` from the repo root.
 */

/** `tools/src/cli.ts` → monorepo root is two levels up. */
const WORKSPACE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");

interface Args {
  describe?: string;
  image?: string;
  name?: string;
  out?: string;
  height?: number;
  help?: boolean;
}

function parseArgs(argv: string[]): Args {
  const args: Args = {};

  for (let i = 0; i < argv.length; i++) {
    const token = argv[i]!;
    // pnpm/npm insert a bare `--` between the script name and user args.
    if (token === "--") continue;
    const next = () => argv[++i];

    switch (token) {
      case "--describe":
      case "-d":
        args.describe = next();
        break;
      case "--image":
      case "-i":
        args.image = next();
        break;
      case "--name":
      case "-n":
        args.name = next();
        break;
      case "--out":
      case "-o":
        args.out = next();
        break;
      case "--height":
        args.height = Number(next());
        break;
      case "--help":
      case "-h":
        args.help = true;
        break;
      default:
        if (token.startsWith("-")) throw new Error(`unknown flag: ${token}`);
    }
  }
  return args;
}

const USAGE = `
psx-harness — generate a rigged, animated PSX character from a reference image

Usage:
  psx-harness --describe "<description>" [options]
  psx-harness --image <path> [options]

Options:
  -d, --describe <text>   Generate the reference through the Grok CLI (image_gen)
  -i, --image <path>      Use an existing reference image (PNG or JPEG)
  -n, --name <name>       Character name                      [default: Character]
  -o, --out <path>        Output .glb path        [default: assets/characters/<name>.glb]
      --height <metres>   Target world height                 [default: 1.7]
  -h, --help              Show this message

The reference should be a full-body front view on a plain background. Generation
is cached by prompt, so re-running the same description costs nothing.
`.trim();

async function main(): Promise<void> {
  // Always run relative to the monorepo root so default outputs land in
  // `assets/characters/` and documented fixture paths resolve.
  if (existsSync(resolve(WORKSPACE_ROOT, "pnpm-workspace.yaml"))) {
    chdir(WORKSPACE_ROOT);
  }

  const args = parseArgs(process.argv.slice(2));

  if (args.help || (!args.describe && !args.image)) {
    console.log(USAGE);
    process.exit(args.help ? 0 : 1);
  }

  const name = args.name ?? "Character";
  const outputPath = args.out ?? defaultOutputPath(name);

  if (args.describe && !args.image) {
    const bridge = new GrokBridge();
    if (!(await bridge.isAvailable())) {
      console.error(
        "Grok CLI not found on PATH. Install it, set GROK_BIN, or pass --image with an existing reference.",
      );
      process.exit(1);
    }
    console.log("Generating reference image (this takes about a minute)…");
  }

  const result = await runPipeline({
    ...(args.image ? { imagePath: args.image } : {}),
    ...(args.describe ? { describe: args.describe } : {}),
    name,
    outputPath,
    ...(args.height ? { height: args.height } : {}),
  });

  console.log(`\n${name}\n${formatReport(result)}\n`);
}

main().catch((error: unknown) => {
  console.error(error instanceof Error ? error.message : String(error));
  process.exit(1);
});
