/** Level-aware `wrnexus test` runner with Bun and optional Playwright backends. */
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { spawn, type ChildProcess } from "node:child_process";
import { join, relative, resolve } from "node:path";

export const TEST_LEVELS = [
  "unit",
  "component",
  "api",
  "browser",
  "visual",
  "accessibility",
  "performance",
] as const;
export type TestLevel = (typeof TEST_LEVELS)[number];
export interface TestCommandPlan {
  command: string;
  args: string[];
  cwd: string;
  level?: TestLevel;
  files: string[];
  setup?: { command: string; args: string[] };
}

function shardFiles(files: string[], value?: string): string[] {
  if (!value) return files;
  const match = /^(\d+)\/(\d+)$/.exec(value);
  if (!match) throw new Error("WRN-TEST-SHARD: expected --shard=<index>/<total>");
  const index = Number(match[1]);
  const total = Number(match[2]);
  if (index < 1 || total < 1 || index > total)
    throw new Error("WRN-TEST-SHARD: index must be between 1 and total");
  return files.filter((_file, position) => position % total === index - 1);
}

function testFiles(root: string): string[] {
  const files: string[] = [];
  const walk = (directory: string): void => {
    if (!existsSync(directory)) return;
    for (const entry of readdirSync(directory, { withFileTypes: true })) {
      if (["node_modules", "dist", ".wrnexus", "coverage"].includes(entry.name)) continue;
      const path = join(directory, entry.name);
      if (entry.isDirectory()) walk(path);
      else if (/\.(?:test|spec)\.[cm]?[jt]sx?$/i.test(entry.name)) files.push(path);
    }
  };
  for (const directory of ["app", "test", "tests"]) walk(join(root, directory));
  return files.sort();
}

function hasPlaywright(root: string): boolean {
  if (
    ["playwright.config.ts", "playwright.config.js", "playwright.config.mjs"].some((file) =>
      existsSync(join(root, file)),
    )
  )
    return true;
  try {
    const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
    return Boolean(
      manifest.dependencies?.["@playwright/test"] || manifest.devDependencies?.["@playwright/test"],
    );
  } catch {
    return false;
  }
}

export function createTestPlan(appRoot: string, args: string[]): TestCommandPlan {
  const root = resolve(appRoot);
  const level = args.find((value): value is TestLevel => TEST_LEVELS.includes(value as TestLevel));
  const watch = args.includes("--watch");
  const shard = args.find((value) => value.startsWith("--shard="))?.slice(8);
  const browsers = (args.find((value) => value.startsWith("--browsers="))?.slice(11) ?? "chromium")
    .split(",")
    .filter(Boolean);
  const passthrough = args.filter(
    (value) =>
      !value.startsWith("--profile=") &&
      value !== "--watch" &&
      value !== appRoot &&
      value !== level &&
      !value.startsWith("--browsers=") &&
      !value.startsWith("--shard=") &&
      value !== "--install-browsers",
  );
  if ((level === "browser" || level === "visual") && hasPlaywright(root)) {
    return {
      command: process.execPath,
      args: [
        "x",
        "playwright",
        "test",
        ...(level === "visual" ? ["--grep", "@visual"] : []),
        ...browsers.flatMap((browser) => ["--project", browser]),
        ...(shard ? [`--shard=${shard}`] : []),
        "--reporter=line,html",
        ...passthrough,
      ],
      cwd: root,
      level,
      files: [],
      ...(args.includes("--install-browsers")
        ? {
            setup: { command: process.execPath, args: ["x", "playwright", "install", ...browsers] },
          }
        : {}),
    };
  }
  const pattern =
    level === "accessibility"
      ? /(?:accessibility|a11y)/i
      : level === "performance"
        ? /(?:performance|benchmark)/i
        : level
          ? new RegExp(level, "i")
          : null;
  const files = shardFiles(
    pattern ? testFiles(root).filter((file) => pattern.test(relative(root, file))) : [],
    shard,
  );
  return {
    command: process.execPath,
    args: ["test", ...(watch ? ["--watch"] : []), ...(pattern ? files : []), ...passthrough],
    cwd: root,
    level,
    files,
  };
}

export function runTests(appRoot: string, args: string[]): ChildProcess | null {
  const plan = createTestPlan(appRoot, args);
  if (plan.level && !plan.files.length && !plan.args.includes("playwright")) {
    console.error(
      `WRN-TEST-NO-FILES: no ${plan.level} tests found. Name a file or directory with '${plan.level}' under app/, test/, or tests/.`,
    );
    process.exitCode = 1;
    return null;
  }
  const launch = () => spawn(plan.command, plan.args, { stdio: "inherit", cwd: plan.cwd });
  if (plan.setup) {
    const setup = spawn(plan.setup.command, plan.setup.args, { stdio: "inherit", cwd: plan.cwd });
    setup.on("exit", (code, signal) => {
      if (signal || code !== 0) {
        process.exitCode = code ?? 1;
        return;
      }
      const test = launch();
      test.on("exit", (testCode, testSignal) => {
        if (!testSignal) process.exitCode = testCode ?? 0;
      });
    });
    return setup;
  }
  const child = launch();
  child.on("exit", (code, signal) => {
    if (!signal) process.exitCode = code ?? 0;
  });
  return child;
}
