import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright";
import pixelmatch from "pixelmatch";
import { PNG } from "pngjs";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");

function arg(name, fallback = "") {
  const index = process.argv.indexOf(`--${name}`);
  return index >= 0 ? process.argv[index + 1] : fallback;
}

function boolArg(name) {
  return process.argv.includes(`--${name}`);
}

function parseViewport(value) {
  const [width, height] = value.split("x").map((item) => Number.parseInt(item, 10));
  if (!width || !height) {
    throw new Error(`Invalid --viewport "${value}". Expected WIDTHxHEIGHT, for example 1440x900.`);
  }
  return { width, height };
}

function ensureUrl(value) {
  if (!value) throw new Error("Missing required URL argument.");
  if (value.startsWith("file://") || value.startsWith("http://") || value.startsWith("https://")) return value;
  return `file://${path.resolve(repoRoot, value)}`;
}

function readPng(file) {
  return PNG.sync.read(fs.readFileSync(file));
}

async function preparePage(page, url, shouldClearStorage, token) {
  await page.goto(url, { waitUntil: "networkidle" });
  if (shouldClearStorage) {
    await page.evaluate(() => {
      localStorage.clear();
      sessionStorage.clear();
    });
    await page.goto(url, { waitUntil: "networkidle" });
  }
  // 注入登录 token,让受保护的真 React 路由 + /exact 镜像都能 hydrate 同一份真实数据
  if (token) {
    await page.evaluate((value) => localStorage.setItem("airshelf_token", value), token);
    await page.goto(url, { waitUntil: "networkidle" });
    await page.waitForTimeout(900); // 等异步数据 hydrate 落定
  }
  await page.evaluate(async () => {
    if ("fonts" in document) await document.fonts.ready;
  });
  await page.addStyleTag({
    content: `
      *, *::before, *::after {
        animation-duration: 0s !important;
        animation-delay: 0s !important;
        transition-duration: 0s !important;
        caret-color: transparent !important;
      }
    `
  });
}

const source = ensureUrl(arg("source"));
const target = ensureUrl(arg("target"));
const name = arg("name", "page");
const viewport = parseViewport(arg("viewport", "1440x900"));
const outDir = path.resolve(repoRoot, arg("out", "core/qa/visual-parity/output"));
const threshold = Number.parseFloat(arg("threshold", "0.03"));
const clearTargetStorage = boolArg("clear-target-storage");
const token = arg("token", "");

fs.mkdirSync(outDir, { recursive: true });

const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport,
  deviceScaleFactor: 1,
  colorScheme: "light",
  reducedMotion: "reduce"
});

const sourcePage = await context.newPage();
const targetPage = await context.newPage();

const sourcePath = path.join(outDir, `${name}.design.png`);
const targetPath = path.join(outDir, `${name}.implementation.png`);
const diffPath = path.join(outDir, `${name}.diff.png`);
const reportPath = path.join(outDir, `${name}.report.json`);

await preparePage(sourcePage, source, false, token);
await preparePage(targetPage, target, clearTargetStorage, token);

await sourcePage.screenshot({ path: sourcePath, fullPage: false });
await targetPage.screenshot({ path: targetPath, fullPage: false });
await browser.close();

const sourcePng = readPng(sourcePath);
const targetPng = readPng(targetPath);

if (sourcePng.width !== targetPng.width || sourcePng.height !== targetPng.height) {
  throw new Error(`Screenshot size mismatch: design ${sourcePng.width}x${sourcePng.height}, implementation ${targetPng.width}x${targetPng.height}`);
}

const diff = new PNG({ width: sourcePng.width, height: sourcePng.height });
const diffPixels = pixelmatch(sourcePng.data, targetPng.data, diff.data, sourcePng.width, sourcePng.height, {
  threshold,
  includeAA: false
});
fs.writeFileSync(diffPath, PNG.sync.write(diff));

const totalPixels = sourcePng.width * sourcePng.height;
const report = {
  name,
  source,
  target,
  viewport,
  threshold,
  diffPixels,
  totalPixels,
  diffRatio: Number((diffPixels / totalPixels).toFixed(6)),
  pass: diffPixels === 0,
  artifacts: {
    design: path.relative(repoRoot, sourcePath),
    implementation: path.relative(repoRoot, targetPath),
    diff: path.relative(repoRoot, diffPath)
  }
};

fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
console.log(JSON.stringify(report, null, 2));
