#!/usr/bin/env node

import { readFileSync } from "node:fs";
import { Command } from "commander";
import chalk from "chalk";
import { checkPlugin, infoTarget, metadataTarget, packPlugin, submitDraft } from "./core.js";

const packageJson = JSON.parse(
  readFileSync(new URL("../package.json", import.meta.url), "utf8"),
) as { version: string };

const program = new Command()
  .name("hf-pack")
  .description("Validate, build, and bundle HaloForge plugins into .hfpkg archives")
  .version(packageJson.version)
  .showSuggestionAfterError();

program
  .command("check")
  .argument("<plugin-dir>", "path to the plugin directory")
  .action(wrap(async (pluginDir: string) => {
    await checkPlugin(pluginDir);
  }));

program
  .command("info")
  .argument("<path>", "path to a plugin directory or .hfpkg archive")
  .action(wrap(async (targetPath: string) => {
    await infoTarget(targetPath);
  }));

program
  .command("pack")
  .argument("<plugin-dir>", "path to the plugin directory")
  .option("-o, --out <dir>", "output directory for the .hfpkg file")
  .option("-r, --release", "build backend and frontend in release mode", false)
  .option("--no-backend", "skip building the Rust backend and package prebuilt native files")
  .option("--no-frontend", "skip building the frontend and package prebuilt frontend files")
  .option("--target <triple>", "Rust compilation target triple")
  .action(wrap(async (pluginDir: string, options: {
    out?: string;
    release?: boolean;
    backend?: boolean;
    frontend?: boolean;
    target?: string;
  }) => {
    await packPlugin(pluginDir, {
      out: options.out,
      release: options.release,
      noBackend: options.backend === false,
      noFrontend: options.frontend === false,
      target: options.target,
    });
  }));

program
  .command("metadata")
  .argument("<path>", "path to a .hfpkg artifact")
  .option("--artifact-url <url>", "public HTTPS download URL for the packaged plugin artifact")
  .option("--source <source>", "catalog source label", "official")
  .option("--slug <slug>", "catalog item slug override")
  .option("--homepage-url <url>", "homepage URL override")
  .option("--repository-url <url>", "repository URL override")
  .option("--signing-key-id <id>", "signing key id to embed in generated metadata")
  .option("--signing-key-base64 <key>", "base64-encoded signing key")
  .option("--signing-key-env <name>", "environment variable containing the signing key", "HF_PLUGIN_SIGNING_PRIVATE_KEY")
  .option("--signature <signature>", "precomputed signature to embed")
  .option("--signature-algorithm <algorithm>", "signature algorithm label", "ed25519")
  .option("-o, --output <path>", "output path for the JSON draft")
  .option("--pretty", "pretty-print JSON output", false)
  .action(wrap(async (targetPath: string, options: {
    artifactUrl?: string;
    source?: string;
    slug?: string;
    homepageUrl?: string;
    repositoryUrl?: string;
    signingKeyId?: string;
    signingKeyBase64?: string;
    signingKeyEnv?: string;
    signature?: string;
    signatureAlgorithm?: string;
    output?: string;
    pretty?: boolean;
  }) => {
    await metadataTarget(targetPath, options);
  }));

program
  .command("submit")
  .argument("<path>", "path to catalog draft JSON generated by `hf-pack metadata`")
  .option("--api-base-url <url>", "HaloForge admin API base URL", "https://admin.haloforge.dev")
  .option("--token <token>", "bearer token value")
  .option("--token-env <name>", "environment variable containing the admin bearer token", "HF_ADMIN_TOKEN")
  .action(wrap(async (draftPath: string, options: {
    apiBaseUrl?: string;
    token?: string;
    tokenEnv?: string;
  }) => {
    await submitDraft(draftPath, options);
  }));

await program.parseAsync(process.argv);

function wrap<T extends unknown[]>(action: (...args: T) => Promise<void>) {
  return async (...args: T): Promise<void> => {
    try {
      await action(...args);
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      console.error(`${chalk.red.bold("error:")} ${message}`);
      process.exitCode = 1;
    }
  };
}
