import fs from "node:fs";
import path from "node:path";

export const defaultPlaywrightLaunchScanRoots = Object.freeze(["scripts", "tools", "web"]);
export const defaultPlaywrightLaunchAllowlist = Object.freeze([
  "scripts/src/browser-launcher.mjs"
]);

const sourceExtensions = new Set([".cjs", ".cts", ".js", ".jsx", ".mjs", ".mts", ".ts", ".tsx"]);
const skippedDirectories = new Set([".git", ".state", ".worktree", "dist", "node_modules"]);
const directChromiumLaunchPattern = new RegExp("\\bchromium\\s*\\.\\s*launch\\s*\\(", "u");

export function findDirectChromiumLaunches(options = {}) {
  const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
  const roots = options.roots ?? defaultPlaywrightLaunchScanRoots;
  const allowlist = new Set(options.allowlist ?? defaultPlaywrightLaunchAllowlist);
  const scannedFiles = [];
  const allowed = [];
  const violations = [];

  for (const root of roots) {
    const absoluteRoot = path.resolve(repoRoot, root);
    if (!fs.existsSync(absoluteRoot)) continue;
    for (const file of collectSourceFiles(absoluteRoot)) {
      const relativePath = normalizeRelative(repoRoot, file);
      scannedFiles.push(relativePath);
      const matches = findMatches(file, relativePath);
      if (matches.length === 0) continue;
      if (allowlist.has(relativePath)) allowed.push(...matches);
      else violations.push(...matches);
    }
  }

  return {
    status: violations.length === 0 ? "pass" : "fail",
    repoRoot,
    roots,
    allowlist: [...allowlist].sort(),
    scannedFileCount: scannedFiles.length,
    allowed,
    violations
  };
}

function collectSourceFiles(directory) {
  const out = [];
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
    if (entry.isDirectory()) {
      if (!skippedDirectories.has(entry.name)) out.push(...collectSourceFiles(path.join(directory, entry.name)));
      continue;
    }
    if (!entry.isFile()) continue;
    const full = path.join(directory, entry.name);
    if (sourceExtensions.has(path.extname(entry.name))) out.push(full);
  }
  return out;
}

function findMatches(file, relativePath) {
  const lines = fs.readFileSync(file, "utf8").split(/\r?\n/u);
  const matches = [];
  lines.forEach((line, index) => {
    if (directChromiumLaunchPattern.test(line)) {
      matches.push({ file: relativePath, line: index + 1, text: line.trim().slice(0, 200) });
    }
  });
  return matches;
}

function normalizeRelative(root, file) {
  return path.relative(root, file).split(path.sep).join("/");
}
