import path from "path";
import { Flags } from "@oclif/core";
import { loadConfigs } from "../config/index.mjs";
import {
  parseFileTimeoutOption,
  parseShardOption,
  parseSuiteOption,
  parseTypeOption,
  parseWorkersOption,
  resolveRequestedFiles,
} from "./args.mjs";
import * as runner from "../runner/index.mjs";
import { createRunReporter } from "./presentation/run-reporter.mjs";

export const sharedFlags = {
  dir: Flags.string({
    description: "Explicit product directory",
  }),
  service: Flags.string({
    description: "Run or inspect only one service",
  }),
};

export const runFlags = {
  ...sharedFlags,
  type: Flags.string({
    char: "t",
    multiple: true,
    description: "Run specific suite type(s): int, e2e, dal, load, pw, all",
  }),
  suite: Flags.string({
    char: "s",
    multiple: true,
    description: "Run specific suite(s)",
  }),
  file: Flags.string({
    char: "f",
    multiple: true,
    description: "Run specific file(s)",
  }),
  workers: Flags.string({
    description: "Number of test executors for the whole run",
  }),
  "file-timeout-seconds": Flags.string({
    description: "Per-file wall-clock timeout in seconds",
  }),
  shard: Flags.string({
    description: "Run only shard i of n at suite granularity",
  }),
  "write-status": Flags.boolean({
    description: "Write a deterministic testkit.status.json snapshot",
    default: false,
  }),
  "allow-partial-status": Flags.boolean({
    description: "Allow --write-status for filtered runs",
    default: false,
  }),
  "ignore-skip-rules": Flags.boolean({
    description: "Run files even if testkit.setup.ts marks them skipped",
    default: false,
  }),
  "output-mode": Flags.string({
    description: "Reporter mode",
    options: ["compact", "debug"],
  }),
  debug: Flags.boolean({
    description: "Alias for --output-mode debug",
    default: false,
  }),
};

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

export async function executeRunCommand(command, flags, positionalType = null) {
  const { allConfigs, configs } = await resolveConfigsForCommand(flags);
  const workers = flags.workers == null ? null : parseWorkersOption(flags.workers);
  const fileTimeoutSeconds =
    flags["file-timeout-seconds"] == null
      ? null
      : parseFileTimeoutOption(flags["file-timeout-seconds"]);
  const shard = parseShardOption(flags.shard);
  const typeValues = parseTypeOption(flags.type, positionalType);
  const suiteSelectors = parseSuiteOption(flags.suite);
  const rawFileNames = Array.isArray(flags.file) ? flags.file : [flags.file].filter(Boolean);
  const productDir = allConfigs[0]?.productDir || process.cwd();
  const fileNames = resolveRequestedFiles(rawFileNames, productDir, process.cwd());
  const outputMode = command.jsonEnabled()
    ? "json"
    : flags.debug
      ? "debug"
      : flags["output-mode"] || "compact";
  const reporter = createRunReporter({ outputMode });
  const result = await runner.runAll(
    configs,
    typeValues,
    suiteSelectors,
    {
      ...flags,
      typeValues,
      fileNames,
      workers,
      fileTimeoutSeconds,
      shard,
      serviceFilter: flags.service || null,
      reporter,
      writeStatus: flags["write-status"],
      allowPartialStatus: flags["allow-partial-status"],
      ignoreSkipRules: flags["ignore-skip-rules"],
    },
    allConfigs
  );
  return {
    outputMode,
    ...result,
  };
}

export async function runStatusLike(commandName, flags) {
  const { allConfigs, configs } = await resolveConfigsForCommand(flags);

  if (commandName === "cleanup") {
    await runner.cleanup(allConfigs[0]?.productDir || process.cwd());
    return { ok: true };
  }

  const productResults = [];
  for (const config of configs) {
    if (commandName === "status") {
      productResults.push(runner.showStatus(config));
      continue;
    }
    await runner.destroy(config);
    productResults.push({ name: config.name, destroyed: true });
  }

  return { ok: true, results: productResults };
}

export function makeKnownFailuresFlags() {
  return {
    ...sharedFlags,
    input: Flags.string({
      description: "Known failures JSON path (repo-relative)",
    }),
    output: Flags.string({
      description: "Output path",
    }),
    status: Flags.string({
      description: "Status artifact path",
    }),
    "issue-mode": Flags.string({
      description: "Issue validation mode override: off, warn, error",
      options: ["off", "warn", "error"],
    }),
  };
}

export function relativeToProduct(productDir, targetPath) {
  return path.relative(productDir, targetPath);
}
