import { execFileSync } from "node:child_process";
import { expect } from "e2e";
import { test, WEB } from "./t3.ts";

test("GitLab links reveal files and select the intended diff side", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("GitLab CLI");
  execFileSync("git", ["remote", "add", "origin", "https://gitlab.com/gitlab-org/cli.git"], { cwd: project });
  const root = "https://gitlab.com/gitlab-org/cli/-/merge_requests/4330/diffs";
  const hash = "0cd3702f060909d3161d1095b8347d4d07671427";
  const cases = [
    ["Control line 255", `#${hash}_250_255`, "control"],
    ["File anchor", `#diff-content-${hash}`, "file-anchor"],
    ["File query", `?file=${hash}`, "file-query"],
    ["Rapid Diffs new line 253", "#line_0cd3702f0_A253", "rapid-diffs"],
    ["Removed old line 107", "#92bc5c585cbecd1fbb0bea92f45a5e7bf927a435_107_106", "removed-line"],
    ["Commit f49808d9", "?commit_id=f49808d9e9123458dbb3c3eca7de55ea7d346dea", "commit-scope"],
  ];
  cases.push(["Commit scope excludes other changes", "https://gitlab.com/gitlab-org/cli/-/merge_requests/1000/diffs?commit_id=1e3cc3dd1c248ae5f67ad3f45693f5d34f8f8322", "multi-commit"]);
  const threads = [];
  for (const [name, suffix] of cases) threads.push(await t3.thread({ project, title: name!, messages: [`[${name}](${suffix!.startsWith("https:") ? suffix : root + suffix})`] }));
  const results: unknown[] = [];
  for (const [index, [name, , label]] of cases.entries()) {
    await t3.open(threads[index]!);
    await screen.getByRole("button", /^Agent \[/).tap();
    await screen.getByRole("link", name).tap();
    await screen.getByRole("button", "Diff scope: All commits").or(screen.getByRole("button", /Diff scope:/)).first().waitFor({ timeout: 120_000 });
    await browser.locator("[data-diffs-header]").first().waitFor();
    await screen.getByRole("button", "Maximize panel").tap();
    if (label === "commit-scope" || label === "multi-commit") {
      await screen.getByRole("button", "Expand all files").tap();
      if (await screen.getByRole("button", "Show file tree").isVisible()) await screen.getByRole("button", "Show file tree").tap();
      await browser.locator("[data-line]").first().waitFor();
    }
    await browser.evaluate(() => new Promise(resolve => setTimeout(resolve, 750)));
    await app.screenshot(label!);
    const state = await browser.evaluate(() => {
      const nodes: Element[] = [];
      function walk(root: Document | ShadowRoot) {
        for (const el of root.querySelectorAll("*")) { nodes.push(el); if (el.shadowRoot) walk(el.shadowRoot); }
      }
      walk(document);
      return {
        fileLeaves: nodes.filter(el => el.getAttribute("data-item-type") === "file").map(el => el.getAttribute("data-item-path")),
        selected: nodes.filter(el => el.hasAttribute("data-selected-line")).map(el => ({ text: el.textContent, attrs: Array.from(el.attributes, a => [a.name, a.value]) })),
        lines: nodes.filter(el => el.hasAttribute("data-line")).map(el => el.textContent),
        scope: nodes.find(el => el.getAttribute("aria-label")?.startsWith("Diff scope:"))?.getAttribute("aria-label"),
      };
    });
    console.log(label, JSON.stringify({ selected: state.selected, scope: state.scope, files: state.fileLeaves, renderedLines: state.lines.length }));
    const selected = (line: string, type?: string) => state.selected.some(el => el.attrs.some(([key, value]) => key === "data-column-number" && value === line) && (!type || el.attrs.some(([key, value]) => key === "data-line-type" && value === type)));
    const ok = label === "control" ? selected("255")
      : label === "rapid-diffs" ? selected("253", "change-addition")
      : label === "removed-line" ? selected("107", "change-deletion")
      : label === "commit-scope" ? state.scope !== "Diff scope: All commits"
      : label === "multi-commit" ? state.scope !== "Diff scope: All commits" && state.fileLeaves.length === 1 && state.lines.some(line => line?.includes("j := j")) && !state.lines.some(line => line?.includes("variables-file"))
      : state.lines.length > 0;
    results.push({ label, ok });
  }
  expect(results).toEqual(cases.map(([, , label]) => ({ label, ok: true })));
});
