import { expect, fakeServerState, gotoWorkbench, saveScreenshot, test } from "../fixtures/test";
import { selectors, sessionTab } from "../fixtures/selectors";

test.describe("durable projector resume", () => {
  test.use({ scenarioId: "projector-resume-from-checkpoint" });

  test("catches up a stale AgentRun trace without read-side repair", async ({ page }, testInfo) => {
    const sessionId = "ses_317f78e1-ed91-4b09-a72a-3668d5469f59";
    const traceId = "trc_f2456023233a485b";

    await gotoWorkbench(page, `/workbench/sessions/${sessionId}`);

    const initialTurn = await page.request.get(`/v1/workbench/turns/${traceId}`);
    expect(initialTurn.status()).toBe(200);
    const initialTurnBody = await initialTurn.json();
    expect(initialTurnBody.turn.status).toBe("running");
    expect(initialTurnBody.turn.projectionStatus).toBe("projecting");
    expect(initialTurnBody.turn.projectionHealth).toBe("projecting");
    expect(initialTurnBody.turn.lastProjectedSeq).toBe(4);

    await expect(page.locator(sessionTab(sessionId))).toHaveAttribute("data-active", "true");
    await expect(page.locator(`${selectors.messageCard}[data-role="agent"]`).last()).toHaveAttribute("data-status", "running");
    await saveScreenshot(page, testInfo, "projector-resume-before-catchup");

    await expect.poll(async () => {
      const response = await page.request.get(`/v1/workbench/turns/${traceId}`);
      if (!response.ok()) return "http-error";
      const payload = await response.json();
      return `${payload.turn?.status}/${payload.turn?.projectionHealth}/${payload.turn?.lastProjectedSeq}`;
    }, { timeout: 12_000 }).toBe("completed/caught-up/7");

    const trace = await page.request.get(`/v1/workbench/traces/${traceId}/events?limit=10`);
    expect(trace.status()).toBe(200);
    const traceBody = await trace.json();
    expect(traceBody.traceStatus).toBe("completed");
    expect(traceBody.projectionStatus).toBe("caught-up");
    expect(traceBody.projectionHealth).toBe("caught-up");
    expect(traceBody.events.map((event: { seq?: number }) => event.seq)).toEqual([1, 2, 3, 4, 5, 6, 7]);
    expect(traceBody.range).toMatchObject({ afterProjectedSeq: 0, fromProjectedSeq: 1, toProjectedSeq: 7, returned: 7, total: 7 });
    expect(traceBody.nextProjectedSeq).toBe(7);
    expect(traceBody).not.toHaveProperty("nextSeq");
    expect(traceBody).not.toHaveProperty("nextSinceSeq");
    expect(traceBody.range).not.toHaveProperty("afterSeq");
    expect(traceBody.range).not.toHaveProperty("fromSeq");
    expect(traceBody.range).not.toHaveProperty("toSeq");

    const emptyTracePage = await page.request.get(`/v1/workbench/traces/${traceId}/events?cursor=projected:999999&limit=5`);
    expect(emptyTracePage.status()).toBe(200);
    const emptyTraceBody = await emptyTracePage.json();
    expect(emptyTraceBody.events).toHaveLength(0);
    expect(emptyTraceBody.range).toMatchObject({ afterProjectedSeq: 999999, fromProjectedSeq: null, toProjectedSeq: null, returned: 0, total: 7 });
    expect(emptyTraceBody.nextProjectedSeq).toBe(999999);
    expect(emptyTraceBody).not.toHaveProperty("nextSeq");
    expect(emptyTraceBody).not.toHaveProperty("nextSinceSeq");
    expect(emptyTraceBody.range).not.toHaveProperty("afterSeq");
    expect(emptyTraceBody.range).not.toHaveProperty("fromSeq");
    expect(emptyTraceBody.range).not.toHaveProperty("toSeq");

    const finalCard = page.locator(`${selectors.messageCard}[data-role="agent"]`).filter({ hasText: "fake projector resume completed from durable checkpoint" });
    await expect(finalCard).toHaveAttribute("data-status", "completed");
    await expect(page.locator(`${selectors.traceTimeline}[data-status="completed"]`).last()).toBeVisible();
    expect(await finalCard.innerText()).not.toContain("暂无可读 Trace");
    await expect(page.locator(selectors.commandSend)).toHaveAttribute("data-action", "turn");
    await saveScreenshot(page, testInfo, "projector-resume-after-catchup");

    const state = await fakeServerState(page) as { legacyRequestLedger?: unknown[]; chatRequests?: unknown[] };
    expect(state.legacyRequestLedger).toEqual([]);
    expect(state.chatRequests).toEqual([]);
  });
});
