import { cac } from "cac";
import { loadConfigs } from "../config/index.mjs";
import {
  parseJobsOption,
  parseShardOption,
  resolveRequestedFiles,
  resolveCliSelection,
  validateFrameworkOption,
} from "./args.mjs";
import * as runner from "../runner/index.mjs";

export function run() {
  const cli = cac("testkit");

  cli
    .command("[first] [second] [third]", "Run test suites (int, e2e, dal, all)")
    .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("--jobs <n>", "Number of isolated worker stacks for the whole run", {
      default: "1",
    })
    .option("--shard <i/n>", "Run only shard i of n at suite granularity")
    .option("--framework <name>", "Filter by framework (default, playwright, all)", {
      default: "all",
    })
    .option("--write-status", "Write a deterministic testkit.status.json snapshot")
    .option("--allow-partial-status", "Allow --write-status for filtered runs")
    .action(async (first, second, third, options) => {
      // Resolve: service filter, suite type, and --dir.
      //
      // From product dir:
      //   testkit                          → all services, all types
      //   testkit int -s health            → all services, int, health
      //   testkit api int                  → one service, int
      //   testkit api                      → one service, all types
      //
      // From workspace root:
      //   testkit --dir my-product int     → all services, int
      //   testkit --dir my-product api int → one service, int

      // Now resolve service vs type from remaining args
      const allConfigs = await loadConfigs({ dir: options.dir });
      const serviceNames = new Set(allConfigs.map((config) => config.name));
      const { service, type } = resolveCliSelection({
        first,
        second,
        serviceNames,
      });
      const configs = service
        ? allConfigs.filter((config) => config.name === service)
        : allConfigs;
      if (service && configs.length === 0) {
        const available = allConfigs.map((config) => config.name).join(", ");
        throw new Error(`Service "${service}" not found. Available: ${available}`);
      }

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

      const framework = validateFrameworkOption(options.framework);

      const jobs = parseJobsOption(options.jobs);
      const shard = parseShardOption(options.shard);

      const suiteType = type || "all";
      const suiteNames = Array.isArray(options.suite) ? options.suite : [options.suite].filter(Boolean);
      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,
        suiteType,
        suiteNames,
        {
          ...options,
          framework,
          fileNames,
          jobs,
          shard,
          serviceFilter: service,
        },
        allConfigs
      );
    });

  cli.help();
  cli.parse();
}
