/// <reference lib="dom" />
import { expect } from "e2e";
import { runPromise, test, WEB } from "./t3.ts";
import { run } from "./shell.ts";

test("Branch labels retain the selected branch through checkout and status refresh", WEB, async ({ t3, app, browser, screen }) => {
  const project = await t3.project("Branch switching", { "README.md": "Synthetic branch-switching demonstration\n" });
  for (const branch of ["feature-a", "feature-b"]) await runPromise(run("git", ["branch", branch], { cwd: project }));
  const thread = await t3.thread({ project, title: "Branch switching", messages: ["first"] });
  await t3.open(thread);
  await screen.getByRole("button", "New thread", { exact: true }).tap();
  await screen.getByRole("combobox", "Workspace", { exact: true }).last().tap();
  await screen.getByRole("option", "Current checkout", { exact: true }).tap();
  await screen.getByRole("combobox", "Branch: main").last().waitFor({ state: "visible" });
  await app.screenshot("initial-current-checkout");
  await browser.evaluate(() => {
    const state = { active: false, target: "", surface: "", start: 0, events: [] as { ms: number; label: string; disabled: boolean }[], results: [] as { surface: string; target: string; events: { ms: number; label: string; disabled: boolean }[]; flipped: boolean }[] };
    Object.assign(window, { branchProof: state });
    const overlay = document.createElement("pre");
    overlay.id = "branch-observation";
    overlay.style.cssText = "position:fixed;left:280px;top:230px;z-index:99999;background:#f4f6fa;color:#172033;border:1px solid #aab4c4;padding:16px;font:14px/1.6 monospace;pointer-events:none;max-width:620px;white-space:pre-wrap";
    overlay.textContent = "Branch label observation — waiting for a switch";
    document.body.append(overlay);
    const sample = () => {
      if (!state.active) return;
      const buttons = [...document.querySelectorAll<HTMLElement>('[aria-label^="Branch: "]')].filter(b => b.getBoundingClientRect().width > 0).sort((a,b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
      const button = state.surface === "composer" ? buttons.at(-1) : buttons[0];
      if (!button) return;
      const label = button.getAttribute("aria-label")!.slice(8);
      const disabled = button.hasAttribute("disabled") || button.getAttribute("aria-disabled") === "true";
      const last = state.events.at(-1);
      if (!last || last.label !== label || last.disabled !== disabled) state.events.push({ ms: Math.round(performance.now() - state.start), label, disabled });
      const content = `Observed ${state.surface} label → ${state.target}\n\n` + state.events.map(e => `${String(e.ms).padStart(4)} ms  ${e.label}${e.disabled ? " (checkout pending)" : ""}`).join("\n");
      if (overlay.textContent !== content) overlay.textContent = content;
    };
    new MutationObserver(sample).observe(document.body, { subtree: true, attributes: true, childList: true, characterData: true });
  });
  const changes: { surface: string; target: string; flipped: boolean; events: unknown }[] = [];
  const switchBranch = async (surface: "composer" | "details", target: string, create = false) => {
    await browser.evaluate((surface) => {
      const buttons = [...document.querySelectorAll<HTMLElement>('[aria-label^="Branch: "]')].filter(b => b.getBoundingClientRect().width > 0).sort((a,b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
      if (buttons.length !== 2) throw new Error(`Expected composer and details pickers, found ${buttons.length}`);
      buttons[0]!.dataset.proofPicker = "details";
      buttons[1]!.dataset.proofPicker = "composer";
    }, surface);
    await browser.locator(`[data-proof-picker="${surface}"]`).tap();
    if (create) await screen.getByRole("combobox", "Search refs...").fill(target);
    await browser.evaluate(({ surface, target }) => {
      const s = (window as any).branchProof;
      Object.assign(s, { active: true, target, surface, start: performance.now(), events: [] });
    }, { surface, target });
    if (create) await screen.getByText(`Create new ref "${target}"`).tap();
    else await screen.getByRole("option", target).tap();
    // Observe the complete checkout/status window, including a late stale status publication.
    const result = await browser.evaluate(async () => {
      await new Promise(resolve => setTimeout(resolve, 1800));
      const s = (window as any).branchProof;
      s.active = false;
      const first = s.events.findIndex((e: any) => e.label === s.target);
      const flipped = first >= 0 && s.events.slice(first + 1).some((e: any) => e.label !== s.target);
      const result = { surface: s.surface, target: s.target, events: s.events, flipped, settled: s.events.at(-1)?.label };
      s.results.push(result);
      return result;
    });
    changes.push(result);
    console.log(JSON.stringify(result));
  };
  for (const target of ["feature-a", "feature-b", "main", "feature-a"]) await switchBranch("composer", target);
  await app.screenshot("composer-switches");
  for (const target of ["feature-b", "main", "feature-a"]) await switchBranch("details", target);
  await app.screenshot("details-switches");
  await switchBranch("composer", "new-branch-x", true);
  await app.screenshot("created-branch");
  for (const target of ["main", "feature-a", "feature-b"]) await switchBranch("composer", target);
  await app.screenshot("final-switch");
  const branch = await runPromise(run("git", ["branch", "--show-current"], { cwd: project }));
  console.log(`Actual checkout: ${branch.stdout.trim()}`);
  await expect(changes.every((change: any) => change.settled === change.target)).toBe(true);
  await expect(changes.filter(change => change.flipped)).toEqual([]);
});
