/// <reference lib="dom" />
import { readFile, writeFile, readdir, utimes } from "node:fs/promises";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { expect } from "e2e";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

test("An open composer recovers an expired image upload when sending", WEB, async ({ t3, app, screen, browser }) => {
  // The synthetic model consumes arbitrary prompt blocks; enable its image capability.
  const replayPath = join(APP_DIR, "apps/server/scripts/dev-replay-agent.ts");
  const replay = await readFile(replayPath, "utf8");
  await writeFile(replayPath, replay.replace("image: false, audio: false", "image: true, audio: false").replace("workspace: session.cwd", 'workspace: "/workspace/image-demo"'));
  await t3.project("image-demo");
  await t3.open();
  await screen.getByRole("textbox").fill("What is in this image?");
  const uploaded = browser.waitForResponse("**/api/attachments/upload**");
  await browser.evaluate(async () => {
    const canvas = document.createElement("canvas");
    canvas.width = 480; canvas.height = 240;
    const ctx = canvas.getContext("2d")!;
    ctx.fillStyle = "#14364b"; ctx.fillRect(0, 0, 480, 240);
    ctx.fillStyle = "#ffca5c"; ctx.fillRect(40, 40, 120, 120);
    ctx.fillStyle = "white"; ctx.font = "24px sans-serif"; ctx.fillText("Synthetic image", 190, 125);
    const blob = await new Promise<Blob>((resolve) => canvas.toBlob((blob) => resolve(blob!), "image/png"));
    const transfer = new DataTransfer();
    transfer.items.add(new File([blob], "image.png", { type: "image/png" }));
    const input = document.querySelector('input[type="file"]') as HTMLInputElement;
    if (!input) throw new Error("No composer file input");
    input.files = transfer.files;
    input.dispatchEvent(new Event("change", { bubbles: true }));
  });
  const response = await uploaded;
  if (response.status !== 204) throw new Error(`Upload failed: ${response.status}`);
  // Run the app's real expiration sweep directly, avoiding its unrelated 15-minute throttle.
  const attachmentsDir = join(T3_HOME, "userdata/attachments");
  const files = (await readdir(attachmentsDir)).filter((name) => name.startsWith("pending-") && name.endsWith(".png"));
  if (files.length !== 1) throw new Error(`Expected one uploaded PNG, got ${files.length}`);
  const expired = new Date(Date.now() - 25 * 60 * 60 * 1000);
  await utimes(join(attachmentsDir, files[0]!), expired, expired);
  const { sweepStalePendingAttachments } = await import(pathToFileURL(join(APP_DIR, "apps/server/src/attachmentStore.ts")).href);
  if (sweepStalePendingAttachments({ attachmentsDir, nowMs: Date.now() }).deleted !== 1) throw new Error("The expiration sweep did not remove the upload");
  await screen.getByRole("button", "Preview image.png").tap();
  await app.screenshot("local-preview-after-expiry");
  await browser.keyboard.press("Escape");
  await app.screenshot("expired-draft");
  await screen.getByRole("textbox").press("Enter");
  // Wait for either rejection or delivery so both sides capture their settled send result.
  for (let i = 0; i < 300; i++) {
    const settled = await browser.evaluate(() => document.body.innerText.includes("cannot be sent") || (document.body.innerText.includes("This turn is a replay.") && !document.querySelector('button[aria-label="Stop generation"]')));
    if (settled) break;
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
  await browser.evaluate(() => {
    for (const element of document.querySelectorAll("*")) {
      if (element instanceof HTMLElement && element.scrollHeight > element.clientHeight) element.scrollTop = 0;
    }
  });
  await app.screenshot("send-result");
  await expect(screen.getByText("This turn is a replay.", { exact: false })).toBeVisible();
});
