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

test("Cite stays centered above a selection in either drag direction", WEB, async ({t3, app, screen, browser}) => {
  const capture = async (label: string) => {
    let previous = await app.screenshot(`${label}-stability`);
    for (let attempt=0; attempt<5; attempt++) {
      await new Promise(resolve => setTimeout(resolve,500));
      const current = await app.screenshot(label);
      if ((await readFile(previous)).equals(await readFile(current))) return;
      previous = current;
    }
    throw new Error(`Capture did not settle: ${label}`);
  };
  const project = await t3.project("selection-demo");
  await t3.thread({project, title: "Selection positioning", messages: ["Show a short response"]});
  await t3.open();
  await screen.getByRole("button", "Selection positioning, Done, selection-demo").tap();
  await screen.getByText("first fixture turn complete", {exact: false}).first().waitFor({state: "visible"});
  await browser.evaluate(() => {
    const text = document.querySelector('[data-assistant-citation-source]')!;
    for(let parent=text.parentElement;parent;parent=parent.parentElement) {
      if(parent.scrollHeight>parent.clientHeight && getComputedStyle(parent).overflowY !== 'visible') parent.scrollTop=0;
    }
  });
  const points = await browser.evaluate(() => {
    const source = [...document.querySelectorAll('[data-assistant-citation-source]')].find(e => e.textContent?.includes('first fixture turn complete'))!;
    const walker = document.createTreeWalker(source, NodeFilter.SHOW_TEXT);
    let node: Node | null;
    while ((node = walker.nextNode())) {
      const start = node.textContent!.indexOf('first fixture turn complete');
      if(start < 0) continue;
      const range = document.createRange();
      range.setStart(node,start); range.setEnd(node,start + 27);
      const rect = range.getBoundingClientRect();
      return {left:rect.left + 1,right:rect.right - 1,y:(rect.top+rect.bottom)/2};
    }
    throw new Error('Response text not found');
  });
  const centered: boolean[] = [];
  for (const reverse of [false,true]) {
    await browser.keyboard.press('Escape');
    await browser.mouse.move(650,350); await browser.mouse.down(); await browser.mouse.up();
    const start = reverse ? points.right : points.left;
    const end = reverse ? points.left : points.right;
    await browser.mouse.move(start,points.y);
    await browser.mouse.down();
    for(let step=1;step<=20;step++) await browser.mouse.move(start+(end-start)*step/20,points.y);
    await browser.mouse.up();
    await screen.getByRole('button','Cite selection in composer').waitFor({state:'visible'});
    await capture(reverse ? 'right-to-left-selection' : 'left-to-right-selection');
    centered.push(await browser.evaluate(() => {
      const button = document.querySelector('[aria-label="Cite selection in composer"]')!.getBoundingClientRect();
      const selected = window.getSelection()!.getRangeAt(0).getBoundingClientRect();
      return Math.abs((button.left+button.right-selected.left-selected.right)/2)<3 && button.bottom<selected.top;
    }));
  }
  await browser.mouse.move(points.left+5,points.y+5);
  await browser.mouse.down(); await browser.mouse.up();
  await capture('click-near-release');
  const citations = await browser.evaluate(() => document.querySelectorAll('[aria-label^="View cited assistant text:"]').length);
  await browser.keyboard.press('Escape');
  await browser.mouse.move(650,350); await browser.mouse.down(); await browser.mouse.up();
  await browser.mouse.move(points.left,points.y); await browser.mouse.down();
  for(let i=1;i<=20;i++) await browser.mouse.move(points.left+(points.right-points.left)*i/20,points.y);
  await browser.mouse.up();
  await screen.getByRole('button','Cite selection in composer').waitFor({state:'visible'});
  await browser.mouse.move(650,350);
  await browser.mouse.wheel(0,65);
  await capture('top-scroll-settling');
  await screen.getByRole('button','Cite selection in composer').waitFor({state:'visible'});
  await capture('top-edge-selection');
  const below = await browser.evaluate(() => {
    const b=document.querySelector('[aria-label="Cite selection in composer"]')!.getBoundingClientRect();
    const r=window.getSelection()!.getRangeAt(0).getBoundingClientRect();
    return b.top>=r.bottom && Math.abs((b.left+b.right-r.left-r.right)/2)<3;
  });
  await browser.keyboard.press('Escape');
  await screen.getByText('End of the synthetic selection example.',{exact:true}).scrollIntoView();
  await screen.getByRole('button','Disable line wrap').tap();
  await browser.evaluate(() => document.querySelector('[data-assistant-citation-source] pre')!.scrollIntoView({block:'center'}));
  await capture('code-position-ready');
  await browser.mouse.move(650,600); await browser.mouse.down(); await browser.mouse.up();
  const codePoints = await browser.evaluate(() => {
    const code=document.querySelector('[data-assistant-citation-source] pre code')!;
    const w=document.createTreeWalker(code,NodeFilter.SHOW_TEXT);
    let n:Node|null;
    while((n=w.nextNode())) if(n.textContent!.includes('visible selected code text')) {
      const r=document.createRange(); r.selectNodeContents(n); const b=r.getBoundingClientRect();
      const pre=code.closest('pre')!.getBoundingClientRect();
      return {left:Math.max(pre.left+20,b.left+1),right:Math.min(pre.right-20,b.right-1),y:(b.top+b.bottom)/2};
    }
    throw new Error('Code text missing');
  });
  await browser.mouse.move(codePoints.left,codePoints.y); await browser.mouse.down();
  for(let i=1;i<=20;i++) await browser.mouse.move(codePoints.left+(codePoints.right-codePoints.left)*i/20,codePoints.y);
  await browser.mouse.up();
  await screen.getByRole('button','Cite selection in composer').waitFor({state:'visible'});
  await capture('code-selection');
  await browser.mouse.move((codePoints.left+codePoints.right)/2,codePoints.y);
  await browser.mouse.wheel(240,0);
  await screen.getByRole('button','Cite selection in composer').waitFor({state:'visible'});
  await capture('code-scrolled');
  expect({centered, citations, below}).toEqual({centered:[true,true],citations:0,below:true});
});
