import { expect } from "e2e";
import { test, WEB } from "./t3.ts";

test(
  "Queued scheduled deliveries follow the current model",
  WEB,
  async ({ t3, app, screen, browser }) => {
    const project = await t3.project("Scheduled delivery demo");
    const thread = await t3.thread({
      project,
      title: "Scheduled model selection",
      messages: ["Keep this turn waiting while two scheduled deliveries queue"],
      model: "question",
    });
    await t3.open(thread);
    const rpc = (tag: string, payload: unknown): Promise<any> =>
      browser.evaluate(
        async ({ tag, payload }) => {
          const ws = new WebSocket(
            `${location.origin.replace("http", "ws")}/ws?orchestrationProtocol=2`,
          );
          await new Promise((resolve, reject) => {
            ws.onopen = resolve;
            ws.onerror = reject;
          });
          return await new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
              ws.close();
              reject(new Error("RPC timeout: " + tag));
            }, 15000);
            ws.onmessage = (e) => {
              for (const line of String(e.data).split("\n").filter(Boolean)) {
                const m = JSON.parse(line);
                if (m._tag === "Exit") {
                  clearTimeout(timer);
                  ws.close();
                  if (m.exit._tag === "Success") resolve(m.exit.value);
                  else reject(new Error(JSON.stringify(m.exit)));
                }
              }
            };
            ws.send(JSON.stringify({ _tag: "Request", id: "1", tag, payload, headers: [] }));
          });
        },
        { tag, payload },
      );
    const read = () => rpc("orchestration.getThreadProjection", { threadId: thread.threadId });
    const command = (value: Record<string, unknown>) =>
      rpc("orchestration.dispatchCommand", {
        commandId: crypto.randomUUID(),
        threadId: thread.threadId,
        ...value,
      });
    const waitFor = async (description: string, predicate: (state: any) => boolean) => {
      const deadline = Date.now() + 20000;
      while (Date.now() < deadline) {
        const state = await read();
        if (predicate(state)) return state;
        await new Promise((r) => setTimeout(r, 100));
      }
      throw new Error("STEP_TIMEOUT: " + description);
    };
    await screen.getByRole("button", "Question", { exact: true }).tap();
    await browser.keyboard.press("Escape");
    const initial = await read();
    const route = await browser.evaluate(() => location.pathname);
    const task = await rpc("scheduledTasks.upsert", {
      title: "Queued model check",
      prompt: "Scheduled delivery: report the current model",
      enabled: false,
      schedule: { type: "interval", everyMs: 86400000 },
      projectId: initial.thread.projectId,
      threadId: thread.threadId,
      threadMode: "continue",
      workspaceStrategy: { type: "root" },
      modelSelection: { instanceId: "devReplay", model: "question" },
      runtimeMode: "full-access",
      interactionMode: "default",
    });
    await app.open("/settings/scheduled-tasks");
    await screen.getByRole("button", "Actions for Queued model check", { exact: true }).tap();
    await screen.getByRole("menuitem", "Run now", { exact: true }).tap();
    await waitFor(
      "first scheduled delivery queued",
      (s) => s.runs.filter((r: any) => r.status === "queued").length === 1,
    );
    await screen.getByRole("button", "Actions for Queued model check", { exact: true }).tap();
    await screen.getByRole("menuitem", "Run now", { exact: true }).tap();
    await waitFor(
      "two scheduled deliveries queued",
      (s) => s.runs.filter((r: any) => r.status === "queued").length === 2,
    );
    await app.open(route);
    await command({ type: "run.interrupt", runId: initial.runs[0].id, holdQueue: true });
    await waitFor(
      "active turn stopped and queue held",
      (s) =>
        s.runs[0].status === "interrupted" &&
        s.runs.filter((r: any) => r.status === "queued" && r.queueHeld).length === 2,
    );
    await screen.getByRole("button", "Question", { exact: true }).tap();
    await screen.getByRole("option", "Showcase", { exact: false }).tap();
    await waitFor("model B selected", (s) => s.thread.modelSelection.model === "showcase");
    await browser.keyboard.press("Escape");
    await browser.evaluate(async () => {
      await new Promise((r) => setTimeout(r, 600));
    });
    await app.screenshot("model-b-selected-queue-held");
    await command({ type: "queue.resume" });
    const first = await waitFor(
      "first scheduled delivery started",
      (s) => s.runs[1].startedAt !== null && s.runs[1].status !== "queued",
    );
    await screen
      .getByRole(
        "button",
        first.thread.modelSelection.model === "showcase" ? "Showcase" : "Question",
        { exact: true },
      )
      .tap();
    await browser.keyboard.press("Escape");
    await waitFor("first delivery emits provider activity", (s) =>
      s.nodes.some((n: any) => n.runId === first.runs[1].id && n.kind !== "root_turn"),
    );
    await command({ type: "run.interrupt", runId: first.runs[1].id, holdQueue: true });
    await waitFor("first delivery stopped", (s) => s.runs[1].status === "interrupted");
    await browser.evaluate(async () => {
      await new Promise((r) => setTimeout(r, 600));
    });
    await app.screenshot("first-delivery-model");
    console.log(
      "FIRST",
      JSON.stringify({ run: first.runs[1].modelSelection, thread: first.thread.modelSelection }),
    );
    await waitFor(
      "remaining scheduled delivery held",
      (s) =>
        s.runs[1].status === "interrupted" && s.runs[2].status === "queued" && s.runs[2].queueHeld,
    );
    await screen
      .getByRole(
        "button",
        first.thread.modelSelection.model === "showcase" ? "Showcase" : "Question",
        { exact: true },
      )
      .tap();
    await screen.getByRole("option", "Plan and reads", { exact: false }).tap();
    await waitFor("model C selected", (s) => s.thread.modelSelection.model === "todo-list");
    await browser.keyboard.press("Escape");
    await browser.evaluate(async () => {
      await new Promise((r) => setTimeout(r, 600));
    });
    await app.screenshot("model-c-selected-queue-held");
    await command({ type: "queue.resume" });
    const second = await waitFor(
      "second scheduled delivery started",
      (s) => s.runs[2].startedAt !== null && s.runs[2].status !== "queued",
    );
    await screen
      .getByRole(
        "button",
        second.thread.modelSelection.model === "todo-list" ? "Plan and reads" : "Question",
        { exact: true },
      )
      .tap();
    await browser.keyboard.press("Escape");
    await waitFor("second delivery emits provider activity", (s) =>
      s.nodes.some((n: any) => n.runId === second.runs[2].id && n.kind !== "root_turn"),
    );
    await command({ type: "run.interrupt", runId: second.runs[2].id, holdQueue: true });
    await waitFor("second delivery stopped", (s) => s.runs[2].status === "interrupted");
    await browser.evaluate(async () => {
      await new Promise((r) => setTimeout(r, 600));
    });
    await app.screenshot("second-delivery-model");
    console.log(
      "SECOND",
      JSON.stringify({ run: second.runs[2].modelSelection, thread: second.thread.modelSelection }),
    );
    await expect({
      firstRun: first.runs[1].modelSelection.model,
      firstSelection: first.thread.modelSelection.model,
      secondRun: second.runs[2].modelSelection.model,
      secondSelection: second.thread.modelSelection.model,
    }).toEqual({
      firstRun: "showcase",
      firstSelection: "showcase",
      secondRun: "todo-list",
      secondSelection: "todo-list",
    });
  },
);
