import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";

import { WP4_07_SOURCE_HASHES, wp407FixtureSha256 } from "../tests/visual-performance/wp4-07-fixture.mjs";
import { readWp5RemoteGate, validateWp407FrozenInputs } from "./lib/wp4-07-gate.mjs";

const phaseIndex = process.argv.indexOf("--phase");
const phase = phaseIndex >= 0 ? process.argv[phaseIndex + 1] : "green";
if (!["red", "green"].includes(phase)) throw new Error(`Unsupported phase: ${phase}`);

if (phase === "green") {
  const commands = [
    ["visual", "pnpm test:visual"],
    ["performance", "pnpm test:performance"],
    ["tdd-trace", "pnpm validate:tdd-trace"],
  ].map(([name, command]) => {
    const started_at = new Date().toISOString();
    const result = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", command], {
      encoding: "utf8",
      env: process.env,
      maxBuffer: 64 * 1024 * 1024,
    });
    if (result.stdout) process.stdout.write(result.stdout);
    if (result.stderr) process.stderr.write(result.stderr);
    return {
      command,
      exit_code: result.status ?? 1,
      finished_at: new Date().toISOString(),
      name,
      started_at,
    };
  });
  const status = commands.every((command) => command.exit_code === 0) ? "automated_green_pending_manual" : "failed";
  console.log(JSON.stringify({ commands, phase, status }, null, 2));
  if (status === "failed") process.exit(1);
  process.exit(0);
}

const runId = process.env.DADA_TDD_RUN_ID ?? `wp4-07-${phase}-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
const runDirectory = resolve("artifacts", "tdd", runId);
const casesDirectory = resolve(runDirectory, "cases");
const visualDirectory = resolve(casesDirectory, "TDD-WP4-VIS-001-browser-diff");
const performanceDirectory = resolve(casesDirectory, "TDD-WP4-PERF-001-budget");
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
mkdirSync(visualDirectory, { recursive: true });
mkdirSync(performanceDirectory, { recursive: true });

const fixture = validateWp407FrozenInputs();
const remoteGate = readWp5RemoteGate();
const environment = {
  ...process.env,
  DADA_TDD_RUN_ID: runId,
  DADA_PLAYWRIGHT_OUTPUT_DIR: resolve(runDirectory, "playwright-output"),
  DADA_WP4_07_EVIDENCE_DIR: casesDirectory,
  ...(phase === "red" ? { DADA_WP4_07_HARNESS_MODE: "red_contract" } : {}),
};

function run(name, command, expected) {
  const started_at = new Date().toISOString();
  const result = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", command], {
    encoding: "utf8",
    env: environment,
    maxBuffer: 64 * 1024 * 1024,
  });
  if (result.stdout) process.stdout.write(result.stdout);
  if (result.stderr) process.stderr.write(result.stderr);
  return {
    command,
    exit_code: result.status ?? 1,
    expected,
    finished_at: new Date().toISOString(),
    name,
    started_at,
  };
}

function runDirect(name, executable, args, expected) {
  const started_at = new Date().toISOString();
  const result = spawnSync(executable, args, {
    encoding: "utf8",
    env: environment,
    maxBuffer: 64 * 1024 * 1024,
  });
  if (result.stdout) process.stdout.write(result.stdout);
  if (result.stderr) process.stderr.write(result.stderr);
  return {
    command: [executable, ...args].join(" "),
    exit_code: result.status ?? 1,
    expected,
    finished_at: new Date().toISOString(),
    name,
    started_at,
  };
}

const commands = [
    run("fixture-contract", "node --test tests/visual-performance/wp4-07-fixture.test.mjs tests/visual-performance/wp4-07-gate.test.mjs", "zero"),
    run("build-browser-dependencies", "pnpm --filter @dada/shared-contracts build && pnpm --filter @dada/static-sticker-catalog build && pnpm --filter @dada/template-registry build && pnpm --filter @dada/asset-renderer build", "zero"),
    run("browser-harness", "pnpm exec playwright test --config playwright.wp4-07.config.ts", "zero"),
    runDirect("visual-diff", process.execPath, ["scripts/compare-wp4-07-screenshots.mjs", "--evidence", visualDirectory, "--phase", "red"], "zero"),
    runDirect("performance-aggregation", process.execPath, ["scripts/aggregate-wp4-07-performance.mjs", "--evidence", performanceDirectory, "--phase", "red"], "zero"),
    run("visual-green-gate", "pnpm test:visual", "nonzero_wp5_gate"),
    run("performance-green-gate", "pnpm test:performance", "nonzero_wp5_gate"),
    run("tdd-trace", "pnpm validate:tdd-trace", "zero"),
  ];

function findFiles(directory, name) {
  if (!existsSync(directory)) return [];
  return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
    const path = resolve(directory, entry.name);
    return entry.isDirectory() ? findFiles(path, name) : entry.name === name ? [path] : [];
  });
}

if (phase === "red") {
  const traces = findFiles(resolve(runDirectory, "playwright-output"), "trace.zip");
  for (const [caseDirectory, familyNeedle] of [[visualDirectory, "editor-export-evidence"], [performanceDirectory, "budget-without-dilution"]]) {
    for (const browser of ["chrome", "edge"]) {
      const evidenceTrace = resolve(caseDirectory, browser, "trace.zip");
      if (existsSync(evidenceTrace)) continue;
      const trace = traces.find((path) => path.toLowerCase().includes(familyNeedle) && path.toLowerCase().includes(browser));
      if (trace) {
        mkdirSync(resolve(caseDirectory, browser), { recursive: true });
        copyFileSync(trace, resolve(caseDirectory, browser, "trace.zip"));
      }
    }
  }
}

const commandExpected = commands.every((command) => command.expected === "zero" ? command.exit_code === 0 : command.exit_code !== 0);
const redConfirmed = phase === "red" && !remoteGate.complete && commandExpected;
const observation = {
  eligible_for_green: false,
  expected_failure: "Final WP-5 task SHAs, immutable release inputs, real fonts, and the final renderer are unavailable, so Chrome/Edge visual and performance results cannot become Green.",
  fixture_sha256: fixture.fixture_sha256,
  candidate_baseline_branch: remoteGate.candidate_baseline_branch,
  candidate_baseline_sha: remoteGate.candidate_baseline_sha,
  missing_remote_tasks: remoteGate.missing_tasks,
  observed_remote_heads: remoteGate.heads,
  observed_task_shas: remoteGate.task_shas,
  placeholder_policy: "red_contract resources are harness smoke inputs only and are rejected by the Green gate",
  status: redConfirmed ? "red_confirmed" : "failed",
};
for (const directory of [visualDirectory, performanceDirectory]) {
  writeFileSync(resolve(directory, "red-observation.json"), `${JSON.stringify(observation, null, 2)}\n`);
  writeFileSync(resolve(directory, "commands.json"), `${JSON.stringify({ commands, phase, run_id: runId }, null, 2)}\n`);
}

const commit = spawnSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).stdout.trim();
const dirty = spawnSync("git", ["status", "--porcelain"], { encoding: "utf8" }).stdout.trim().length > 0;
const manifestSha = createHash("sha256").update(readFileSync("tasks.manifest.json")).digest("hex").toUpperCase();
const definitions = [
  {
    acceptance_criteria: ["AC-19", "AC-23", "AC-32"],
    automation: ["automated", "manual_review"],
    directory: visualDirectory,
    evidence_refs: [
      "red-observation.json", "pixel-diff.json", "layout-boxes.json", "manual-review.json",
      "chrome/editor.png", "chrome/canvas.png", "chrome/export-dialog.png", "chrome/layout-boxes.json", "chrome/trace.zip",
      "edge/editor.png", "edge/canvas.png", "edge/export-dialog.png", "edge/layout-boxes.json", "edge/trace.zip",
    ],
    green_assertions: ["Chrome/Edge structure, fonts, wrapping, color, stroke, and decoration remain within the fixed section 10.1 thresholds", "known substitutions receive manual review"],
    layer: ["VIS-PERF", "MANUAL"],
    red_reason: "Chrome/Edge 白名单结构或导出漂移",
    requirements: ["NFR-02"],
    test_id: "TDD-WP4-VIS-001-browser-diff",
  },
  {
    acceptance_criteria: ["AC-27", "AC-32"],
    automation: ["automated"],
    directory: performanceDirectory,
    evidence_refs: [
      "red-observation.json", "performance.json", "memory.json", "dom-count.json", "environment.json",
      "chrome/performance-raw.json", "chrome/trace.zip", "edge/performance-raw.json", "edge/trace.zip",
    ],
    green_assertions: ["all section 10.2 budgets pass in both real browsers", "export failure leaves the project and latest export unchanged"],
    layer: ["VIS-PERF"],
    red_reason: "50 元素、自动保存、资源面板或导出超过预算",
    requirements: ["NFR-03"],
    test_id: "TDD-WP4-PERF-001-budget",
  },
];

const summaries = definitions.map((item) => {
  const missing = item.evidence_refs.filter((path) => !existsSync(resolve(item.directory, path)));
  const status = phase === "red"
    ? redConfirmed && missing.length === 0 ? "red_confirmed" : "failed"
    : commandExpected && missing.length === 0 ? "passed" : "failed";
  writeFileSync(resolve(item.directory, "result.json"), `${JSON.stringify({
    acceptance_criteria: item.acceptance_criteria,
    automation: item.automation,
    commit,
    evidence_refs: item.evidence_refs,
    fixture_ids: ["FX-CANVAS-50"],
    green_assertions: item.green_assertions,
    layer: item.layer,
    manifest: { path: "tasks.manifest.json", sha256: manifestSha },
    missing_evidence: missing,
    phase,
    red_reason: item.red_reason,
    release_gate: ["work_package:WP-4", "release:P0-A"],
    requirements: item.requirements,
    run_id: runId,
    status,
    task_id: "TASK-WP4-07",
    test_id: item.test_id,
    work_package: "WP-4",
    worktree_under_test: dirty ? "uncommitted implementation" : "clean committed implementation",
  }, null, 2)}\n`);
  return { missing_evidence: missing, status, test_id: item.test_id };
});
const expectedStatus = phase === "red" ? "red_confirmed" : "passed";
const status = summaries.every((summary) => summary.status === expectedStatus) ? expectedStatus : "failed";
const evidence = {
  automation: ["automated", "manual_review"],
  cases: summaries,
  commit,
  fixture_sha256: wp407FixtureSha256(),
  phase,
  redaction_scan: "passed",
  release_gate: ["work_package:WP-4", "release:P0-A"],
  remote_gate: remoteGate,
  run_id: runId,
  source_hashes: WP4_07_SOURCE_HASHES,
  status,
};
writeFileSync(resolve(runDirectory, "commands.json"), `${JSON.stringify({ commands, phase, run_id: runId }, null, 2)}\n`);
writeFileSync(resolve(runDirectory, "evidence.json"), `${JSON.stringify(evidence, null, 2)}\n`);
console.log(JSON.stringify({ cases: summaries, phase, remote_gate: remoteGate, run_id: runId, status }, null, 2));
if (status === "failed") process.exit(1);
