import { expect } from "e2e";
import { chromium } from "playwright-core";
import { test, WEB } from "./t3.ts";

test("A mounted chat releases historical thread projections after streaming and switching", WEB, async ({t3, app, screen, browser}) => {
  const project = await t3.project("Memory demonstration");
  const updated = await t3.thread({project, title:"Streaming thread", messages:["Initial history"], model:"showcase"});
  const other = await t3.thread({project, title:"Other thread", messages:["Other history"], model:"showcase"});
  await t3.open(other);
  const remote = await chromium.connectOverCDP("http://127.0.0.1:19238");
  const page = remote.contexts().flatMap(c => c.pages()).find(p => p.url().includes(other.threadId))!;
  const cdp = await page.context().newCDPSession(page);
  await browser.evaluate(() => { (window as any).__retention = []; });
  for (let round = 0; round < 6; round++) {
    await screen.getByRole("button", "Streaming thread", {exact:false}).first().tap();
    await screen.getByRole("textbox").fill(`Memory round ${round + 1}`);
    await screen.getByRole("textbox").press("Enter");
    await expect(screen.getByRole("button", "Stop generation")).toBeVisible();
    await expect(screen.getByRole("button", "Stop generation")).toBeHidden({timeout:60000});
    await browser.evaluate(async (threadId) => {
      const {appAtomRegistry} = await import(/* @vite-ignore */ "/src/rpc/atomRegistry.ts");
      const {environmentThreadDetails} = await import(/* @vite-ignore */ "/src/state/threads.ts");
      const environmentId = location.pathname.split("/")[1];
      const value = appAtomRegistry.get(environmentThreadDetails.quietThreadAtom({environmentId, threadId}));
      if (!value?.projection) throw new Error("No loaded thread projection");
      (window as any).__retention.push(new WeakRef(value.projection));
    }, updated.threadId);
    await screen.getByRole("button", "Other thread", {exact:false}).first().tap();
    await screen.getByRole("textbox").fill(`Draft after round ${round + 1}`);
  }
  await cdp.send("HeapProfiler.collectGarbage");
  const kept = await browser.evaluate(() => {
    const refs = (window as any).__retention as WeakRef<object>[];
    const retained = refs.slice(0,-2).flatMap((ref, i) => ref.deref() ? [i] : []);
    const panel = document.createElement("aside");
    panel.style.cssText = "position:fixed;z-index:999999;bottom:24px;right:24px;width:460px;padding:24px;background:#101827;color:#fff;border:2px solid #7b91af;border-radius:12px;font:16px/1.6 monospace;box-shadow:0 4px 30px #0008";
    const rows = refs.map((ref,i) => `<div>Round ${i+1}: ${ref.deref() ? 'RETAINED' : 'collected'}${i>=4?' (recent; excluded)':''}</div>`).join('');
    panel.innerHTML = `<b>Historical projection retention</b><div>Real app · Chromium · 6 streamed turns</div><div>Switched threads after every turn</div><div>Forced GC: HeapProfiler.collectGarbage</div><hr>${rows}<hr><b>Old projections retained: ${retained.length} / 4</b><div>Expected: 0 / 4</div><small>Read-only WeakRef diagnostic; latest two excluded.</small>`;
    document.body.append(panel);
    return retained;
  });
  await app.screenshot("historical-projections-after-gc");
  await cdp.detach();
  await remote.close();
  expect(kept).toEqual([]);
});
