import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { basename, join, relative, resolve } from "node:path";
import { diagnose } from "@wrnexus/compiler";
import { currentCliVersion } from "./update-notifier.ts";

const SENSITIVE_KEY =
  /(?:secret|token|password|passwd|credential|api[-_]?key|private[-_]?key|cookie|authorization|session|dsn|database[-_]?url)/i;
function sanitizeText(value: string): string {
  return value
    .replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[REDACTED_EMAIL]")
    .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, "[REDACTED_IP]")
    .replace(/https?:\/\/[^\s"'`/]+/gi, (origin) =>
      /localhost|127\.0\.0\.1|example\.(?:com|test|org)/i.test(origin)
        ? origin
        : "https://[REDACTED_DOMAIN]",
    )
    .replace(/\b(?:sk|pk|wrn|ghp|xox[baprs])[_-][A-Za-z0-9_-]{12,}\b/g, "[REDACTED_TOKEN]")
    .replace(
      /((?:secret|token|password|apiKey|authorization|cookie)\s*[:=]\s*)(["'`])[^"'`]*\2/gi,
      "$1$2[REDACTED]$2",
    )
    .slice(0, 512 * 1024);
}
function sanitizeValue(value: unknown, key = ""): unknown {
  if (SENSITIVE_KEY.test(key)) return "[REDACTED]";
  if (typeof value === "string") return sanitizeText(value);
  if (Array.isArray(value)) return value.slice(0, 200).map((item) => sanitizeValue(item));
  if (value && typeof value === "object")
    return Object.fromEntries(
      Object.entries(value)
        .slice(0, 500)
        .map(([name, item]) => [name, sanitizeValue(item, name)]),
    );
  return value;
}
function packageVersions(root: string) {
  const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as {
    dependencies?: Record<string, string>;
    devDependencies?: Record<string, string>;
  };
  return Object.fromEntries(
    Object.entries({ ...manifest.dependencies, ...manifest.devDependencies }).sort(
      ([left], [right]) => left.localeCompare(right),
    ),
  );
}
export interface ReproductionReportOptions {
  file?: string;
  error?: string;
  output?: string;
  command?: string[];
}
export function generateReproductionReport(
  appRoot: string,
  options: ReproductionReportOptions = {},
) {
  const root = resolve(appRoot);
  const selected = options.file ? resolve(root, options.file) : undefined;
  if (
    selected &&
    (!relative(root, selected) ||
      relative(root, selected).startsWith("..") ||
      !existsSync(selected))
  )
    throw new Error("WRN-REPORT-FILE: selected source must exist inside the application.");
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
  const output = resolve(root, options.output ?? join(".wrnexus", "reports", stamp));
  if (!relative(root, output) || relative(root, output).startsWith(".."))
    throw new Error("WRN-REPORT-OUTPUT: output must stay inside the application.");
  mkdirSync(output, { recursive: true });
  let source: string | undefined;
  let diagnostics: unknown[] = [];
  if (selected) {
    source = readFileSync(selected, "utf8");
    diagnostics = selected.endsWith(".wrn")
      ? diagnose(source, { file: basename(selected), accessibility: true })
      : [];
    writeFileSync(
      join(output, `sanitized-${basename(selected)}`),
      `${sanitizeText(source)}\n`,
      "utf8",
    );
  }
  const configNames = ["wrnexus.config.ts", "wrnexus.config.js", "wrnexus.config.mjs"];
  const configFile = configNames.map((name) => join(root, name)).find(existsSync);
  const commands = options.command?.length
    ? options.command
    : [
        "bun install --frozen-lockfile",
        selected ? "bunx wrnexus typecheck ." : "bunx wrnexus doctor .",
        "bunx wrnexus build .",
      ];
  const report = sanitizeValue({
    schemaVersion: 1,
    frameworkVersion: currentCliVersion(),
    runtime: { bun: Bun.version, platform: process.platform, architecture: process.arch },
    source: selected ? relative(root, selected).replace(/\\/g, "/") : undefined,
    diagnostics,
    dependencies: packageVersions(root),
    config: configFile ? sanitizeText(readFileSync(configFile, "utf8")) : undefined,
    error: options.error,
    commands,
  });
  const reportFile = join(output, "report.json");
  writeFileSync(reportFile, `${JSON.stringify(report, null, 2)}\n`, "utf8");
  writeFileSync(
    join(output, "README.md"),
    `# Sanitized WRNexus reproduction\n\nGenerated by WRNexus ${currentCliVersion()}. Review the bundle before sharing. Values matching secrets, credentials, emails, IPs and non-public domains are redacted.\n\n## Reproduce\n\n${commands.map((command) => `- \`${command}\``).join("\n")}\n`,
    "utf8",
  );
  return {
    directory: output,
    reportFile,
    sourceFile: selected ? join(output, `sanitized-${basename(selected)}`) : undefined,
  };
}
export function runReport(appRoot: string, args: string[]) {
  const option = (name: string) =>
    args.find((value) => value.startsWith(`--${name}=`))?.slice(name.length + 3);
  const result = generateReproductionReport(appRoot, {
    file: option("file"),
    error: option("error"),
    output: option("output"),
  });
  console.log(`✓ Sanitized reproduction bundle: ${result.directory}`);
  return result;
}
