import { writeFile } from "node:fs/promises";
import { expect } from "e2e";
import { test, WEB } from "./t3.ts";

test("Diff highlights strengthen with Appearance contrast", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("contrast-demo", { "greeting.ts": 'export const greeting = "Hello world";\nexport const color = "red";\nexport const obsolete = true;\n' });
  await t3.thread({ project, title: "Diff contrast example", messages: ["Prepare the example"], model: "multi-turn" });
  await writeFile(`${project}/greeting.ts`, 'export const greeting = "Hello everyone";\nexport const color = "blue";\nexport const added = 42;\n');
  await browser.emulateMedia({ reducedMotion: "reduce" });
  await t3.open();
  await screen.getByRole("button", "Diff contrast example, Done, contrast-demo").tap();
  await browser.keyboard.press("Control+d");
  await screen.getByRole("button", "Maximize panel").tap();
  await screen.getByRole("button", "Expand greeting.ts", { exact: true }).tap();
  await browser.locator("diffs-container [data-line][data-line-type=change-addition]").first().waitFor();
  const samples: Record<string, Record<string, number>> = {};
  for (const mode of ["dark", "light"] as const) {
    await screen.getByRole("button", "Settings", { exact: true }).tap();
    await browser.locator('[data-settings-section="/settings/appearance"]').tap();
    await screen.getByRole("button", `Use ${mode} mode`, { exact: true }).tap();
    for (const scheme of ["red-green", "blue-orange"] as const) {
      await browser.locator('[data-settings-section="/settings/diffs"]').tap();
      await screen.getByRole("combobox", "Diff colors", { exact: true }).tap();
      await screen.getByRole("option", scheme === "red-green" ? "Red & green (default)" : "Blue & orange", { exact: true }).tap();
      for (const [index, contrast] of [100, 150, 200, 100].entries()) {
        await browser.locator('[data-settings-section="/settings/appearance"]').tap();
        const slider = screen.getByRole("slider", "Contrast", { exact: true });
        await slider.press("Home");
        for (let n = 50; n < contrast; n += 5) await slider.press("ArrowRight");
        await screen.getByRole("button", "Close", { exact: true }).tap();
        await screen.getByRole("dialog", "Settings", { exact: true }).waitFor({ state: "hidden" });
        await browser.locator("diffs-container [data-line][data-line-type=change-addition]").first().waitFor();
        await screen.getByText("Showing tracked changes. Checking untracked files...", { exact: true }).waitFor({ state: "hidden" });
        await browser.evaluate(async () => { await document.fonts.ready; const diff = document.querySelector("diffs-container")!.shadowRoot!.querySelector("[data-diff]")!; getComputedStyle(diff).opacity; await Promise.all(diff.getAnimations({subtree:true}).filter(a => a.effect?.getTiming().iterations !== Infinity).map(a => a.finished.catch(() => {}))); });
        const label = `${mode}-${scheme}-${index === 3 ? "reset-100" : contrast}`;
        samples[label] = await browser.evaluate(() => {
          const root = document.querySelector("diffs-container")!.shadowRoot!;
          const diff = root.querySelector("[data-diff]")!;
          const canvas = document.createElement("canvas");
          canvas.width = canvas.height = 1;
          const context = canvas.getContext("2d")!;
          const rgb = (color: string) => {
            context.clearRect(0, 0, 1, 1);
            context.fillStyle = color;
            context.fillRect(0, 0, 1, 1);
            return [...context.getImageData(0, 0, 1, 1).data];
          };
          const base = rgb(getComputedStyle(diff).backgroundColor);
          const distance = (selector: string) => {
            const color = rgb(getComputedStyle(root.querySelector(selector)!).backgroundColor);
            return Math.hypot(...color.slice(0, 3).map((channel, i) => channel - base[i]!));
          };
          const alpha = (kind: string) => {
            const color = getComputedStyle(root.querySelector(`[data-line-type="change-${kind}"] [data-diff-span]`)!).backgroundColor;
            return Number(color.match(/\/\s*([\d.]+)/)?.[1] ?? 1);
          };
          return {
            additionRow: distance('[data-line][data-line-type="change-addition"]'),
            deletionRow: distance('[data-line][data-line-type="change-deletion"]'),
            additionGutter: distance('[data-column-number][data-line-type="change-addition"]'),
            deletionGutter: distance('[data-column-number][data-line-type="change-deletion"]'),
            additionWordAlpha: alpha("addition"),
            deletionWordAlpha: alpha("deletion"),
          };
        });
        await app.screenshot(label);
        await screen.getByRole("button", "Settings", { exact: true }).tap();
      }
    }
    await screen.getByRole("button", "Close", { exact: true }).tap();
        await screen.getByRole("dialog", "Settings", { exact: true }).waitFor({ state: "hidden" });
  }
  console.log("SAMPLES", JSON.stringify(samples));
  for (const mode of ["dark", "light"]) {
    for (const scheme of ["red-green", "blue-orange"]) {
      const sample = (contrast: string | number) => samples[`${mode}-${scheme}-${contrast}`]!;
      for (const part of ["additionRow", "deletionRow", "additionGutter", "deletionGutter"]) {
        expect(sample(150)[part], `${mode} ${scheme} ${part} at 150%`).toBeGreaterThan(sample(100)[part]!);
        expect(sample(200)[part], `${mode} ${scheme} ${part} at 200%`).toBeGreaterThan(sample(150)[part]!);
      }
      for (const part of ["additionWordAlpha", "deletionWordAlpha"]) {
        if (mode === "dark") {
          expect(sample(150)[part]).toBeGreaterThan(sample(100)[part]!);
          expect(sample(200)[part]).toBeGreaterThan(sample(150)[part]!);
        } else {
          for (const contrast of [100, 150, 200]) expect(sample(contrast)[part]).toBe(0.15);
        }
      }
      expect(sample("reset-100")).toEqual(sample(100));
    }
  }
});
