import { cac } from "cac";
import { loadConfigs } from "../config/index.mjs";
import {
  runKnownFailuresRenderCommand,
  runKnownFailuresValidateCommand,
} from "./known-failures.mjs";
import { runDatabaseSnapshotCaptureCommand } from "./db.mjs";
import {
  parseFileTimeoutOption,
  parseShardOption,
  parseSuiteOption,
  parseTypeOption,
  parseWorkersOption,
  resolveRequestedFiles,
  resolveCliSelection,
} from "./args.mjs";
import * as runner from "../runner/index.mjs";

export async function run(argv = process.argv) {
  const cli = cac("testkit");

  cli
    .command("[first] [second] [third]", "Run test suites")
    .option("--service <name>", "Run only one service")
    .option("-t, --type <name>", "Run specific suite type(s): int, e2e, dal, load, pw, all", {
      default: [],
    })
    .option("-s, --suite <name>", "Run specific suite(s)", { default: [] })
    .option("-f, --file <path>", "Run specific file(s)", { default: [] })
    .option("--dir <path>", "Explicit product directory")
    .option("--workers <n>", "Number of test executors for the whole run")
    .option("--file-timeout-seconds <n>", "Per-file wall-clock timeout in seconds")
    .option("--shard <i/n>", "Run only shard i of n at suite granularity")
    .option("--write-status", "Write a deterministic testkit.status.json snapshot")
    .option("--allow-partial-status", "Allow --write-status for filtered runs")
    .option("--input <path>", "Known failures JSON path (repo-relative)")
    .option("--output <path>", "Output path for render/snapshot commands")
    .option("--status <path>", "Status artifact path for validation commands")
    .option("--issue-mode <mode>", "Issue validation mode override: off, warn, error")
    .option(
      "--ignore-skip-rules",
      "Run files even if testkit.setup.ts marks them skipped"
    )
    .action(async (first, second, third, options) => {
      const { lifecycle, positionalType, knownFailuresAction, dbAction } = resolveCliSelection({
        first,
        second,
        third,
      });
      if (knownFailuresAction === "render") {
        await runKnownFailuresRenderCommand(options);
        return;
      }
      if (knownFailuresAction === "validate") {
        await runKnownFailuresValidateCommand(options);
        return;
      }
      if (dbAction === "snapshot-capture") {
        await runDatabaseSnapshotCaptureCommand(options);
        return;
      }

      const allConfigs = await loadConfigs({ dir: options.dir });
      const configs = options.service
        ? allConfigs.filter((config) => config.name === options.service)
        : allConfigs;
      if (options.service && configs.length === 0) {
        const available = allConfigs.map((config) => config.name).join(", ");
        throw new Error(`Service "${options.service}" not found. Available: ${available}`);
      }

      // Lifecycle commands
      if (lifecycle === "cleanup") {
        await runner.cleanup(allConfigs[0]?.productDir || process.cwd());
        return;
      }

      if (lifecycle === "status" || lifecycle === "destroy") {
        for (const config of configs) {
          if (configs.length > 1) console.log(`\n── ${config.name} ──`);
          if (lifecycle === "status") runner.showStatus(config);
          else await runner.destroy(config);
        }
        return;
      }

      const workers = options.workers == null ? null : parseWorkersOption(options.workers);
      const fileTimeoutSeconds =
        options.fileTimeoutSeconds == null
          ? null
          : parseFileTimeoutOption(options.fileTimeoutSeconds);
      const shard = parseShardOption(options.shard);
      const typeValues = parseTypeOption(options.type, positionalType);
      const suiteSelectors = parseSuiteOption(options.suite);
      const rawFileNames = Array.isArray(options.file) ? options.file : [options.file].filter(Boolean);
      const productDir = allConfigs[0]?.productDir || process.cwd();
      const fileNames = resolveRequestedFiles(rawFileNames, productDir, process.cwd());
      await runner.runAll(
        configs,
        typeValues,
        suiteSelectors,
        {
          ...options,
          typeValues,
          fileNames,
          workers,
          fileTimeoutSeconds,
          shard,
          serviceFilter: options.service || null,
        },
        allConfigs
      );
    });

  cli.help();
  const parsed = cli.parse(argv, { run: false });
  await cli.runMatchedCommand();
  return parsed;
}
