import { CliError } from "./core/errors.js";
import { handlers } from "./handlers.js";
import { helpPayload } from "./help/index.js";
import { failure, json } from "./core/output.js";
import type { RuntimeContext } from "./core/types.js";

export async function dispatch(ctx: RuntimeContext | null, argv: string[]): Promise<void> {
  if (argv.length === 0 || argv.includes("--help")) {
    process.stdout.write("Usage: tjwater-cli [OPTIONS] COMMAND [ARGS]...\n\nStructured JSON:\n  tjwater-cli help\n");
    return;
  }
  if (argv[0] === "help") {
    const payload = helpPayload(argv.slice(1));
    if (!payload) {
      failure({
        summary: "未找到命令",
        code: "COMMAND_NOT_FOUND",
        message: `unknown command path: ${argv.slice(1).join(" ")}`,
        retryable: false,
        data: { usage: "tjwater-cli help <command-path>", examples: ["tjwater-cli help simulation run", "tjwater-cli simulation help"] },
        nextCommands: ["tjwater-cli help", "tjwater-cli help simulation"],
      });
      return;
    }
    json(payload);
    return;
  }
  const matched = matchCommand(argv);
  if (!matched) throw new CliError("未找到命令", "COMMAND_NOT_FOUND", `No such command: ${argv.join(" ")}`, 2, false, { usage: "tjwater-cli help" }, ["tjwater-cli help"]);
  const handler = handlers[matched.path];
  if (!handler) throw new CliError("未找到命令", "COMMAND_NOT_FOUND", `No such command: ${argv.join(" ")}`, 2, false, { usage: "tjwater-cli help" }, ["tjwater-cli help"]);
  if (!ctx && matched.path !== "__noop") throw new CliError("CLI 参数错误", "RUNTIME_REQUIRED", "runtime context is required for command execution", 2);
  await handler(ctx!, matched.args);
}

function matchCommand(argv: string[]): { path: string; args: string[] } | null {
  const paths = Object.keys(handlers).sort((a, b) => b.split(" ").length - a.split(" ").length);
  for (const path of paths) {
    const parts = path.split(" ");
    if (parts.every((part, index) => argv[index] === part)) return { path, args: argv.slice(parts.length) };
  }
  if (argv.at(-1) === "help") {
    const payload = helpPayload(argv.slice(0, -1));
    if (payload) {
      json(payload);
      return { path: "__noop", args: [] };
    }
  }
  return null;
}
