import { expect } from "e2e";
import { test, WEB, makeState, runPromise } from "./t3.ts";
import { T3_HOME } from "../e2e.config.ts";

test("Live timers use elapsed time across clock skew and optimistic dispatch", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("Timer demo");
  const thread = await t3.thread({ project, title: "Clock skew", messages: ["first"], model: "showcase" });
  await t3.open(thread);
  const route = await browser.url();
  await browser.addInitScript(() => {
    const shift = Number(sessionStorage.getItem("clockShift") ?? 0);
    const OriginalDate = Date;
    const ClockDate = class extends OriginalDate {
      constructor(...args: any[]) { if (args.length === 0) super(OriginalDate.now() + shift); else super(...args as [string]); }
      static now() { return OriginalDate.now() + shift; }
    };
    window.Date = ClockDate as DateConstructor;
    const OriginalSocket = WebSocket;
    window.WebSocket = class extends OriginalSocket {
      constructor(...args: ConstructorParameters<typeof WebSocket>) {
        super(...args);
        if (sessionStorage.getItem("legacy") === "1") {
          const add = this.addEventListener.bind(this);
          this.addEventListener = ((type: string, listener: any, options: any) => {
            if (type !== "message") return add(type as any, listener, options);
            return add("message", (event) => {
              const found = typeof event.data === "string" && event.data.includes('"serverTime"'); if (found) (window as any).legacyRemoved = ((window as any).legacyRemoved ?? 0) + 1;
              const data = typeof event.data === "string" ? event.data.replace(/"serverTime"\s*:\s*"[^"]*",?/g, "").replace(/,}/g, "}") : event.data;
              listener(new MessageEvent("message", { data }));
            }, options);
          }) as any;
        }
      }
      send(data: any) {
        const w = window as any;
        if (w.holdDispatch && typeof data === "string" && data.includes("orchestration.dispatchCommand")) {
          (w.held ??= []).push(() => super.send(data));
          return;
        }
        return super.send(data);
      }
    };
  });
  const results: Record<string, boolean> = {};
  const { cli } = makeState(T3_HOME);
  for (const [label, shift, legacy] of [["server-ahead", -3600000, false], ["server-behind", 3600000, false], ["older-host", 0, true]] as const) {
    await browser.evaluate(({shift, legacy}) => { sessionStorage.setItem("clockShift", String(shift)); sessionStorage.setItem("legacy", legacy ? "1" : "0"); }, {shift, legacy});
    await app.open(route);
    await screen.getByRole("textbox").fill("");
    await runPromise(cli(["thread", "send", thread.threadId, "again"]));
    const samples = await browser.evaluate(async () => {
      const samples: string[] = [];
      for (let i = 0; i < 13; i++) { samples.push(document.body.innerText); await new Promise(r => setTimeout(r, 250)); }
      return samples;
    });
    await app.screenshot(label);
    console.log(label + " legacy stripped", await browser.evaluate(() => (window as any).legacyRemoved ?? 0));
    console.log(label, JSON.stringify(samples.map(t => t.match(/Working(?: for)? [^\n]+/g))));
    results[label] = samples.some(t => /Working for [1-4]s/.test(t)) && !samples.some(t => /Working for (?:59m|1h)/.test(t));
    await screen.getByRole("button", "Stop generation").waitFor({ state: "hidden", timeout: 30000 });
  }
  await browser.evaluate(() => { sessionStorage.setItem("clockShift", "-3600000"); sessionStorage.setItem("legacy", "0"); });
  await app.open(route);
  await screen.getByRole("textbox").fill("Optimistic dispatch");
  await browser.evaluate(() => { (window as any).holdDispatch = true; });
  await screen.getByRole("textbox").press("Enter");
  const optimistic = await browser.evaluate(async () => {
    const samples: string[] = [];
    for (let i = 0; i < 13; i++) { samples.push(document.body.innerText); await new Promise(r => setTimeout(r, 250)); }
    return { samples, held: ((window as any).held ?? []).length };
  });
  await app.screenshot("optimistic-before-ack");
  console.log("optimistic", JSON.stringify({ held: optimistic.held, timers: optimistic.samples.map(t => t.match(/Working(?: for)? [^\n]+/g)) }));
  results.optimistic = optimistic.held > 0 && optimistic.samples.some(t => /Working for [1-4]s/.test(t));
  await browser.evaluate(() => { const w = window as any; w.holdDispatch = false; for (const send of w.held ?? []) send(); });
  await screen.getByRole("button", "Stop generation").waitFor({ state: "hidden", timeout: 30000 });
  expect(results).toEqual({ "server-ahead": true, "server-behind": true, "older-host": true, optimistic: true });
});
