import { execFileSync } from "node:child_process";
import { setTimeout } from "node:timers/promises";
import { expect } from "e2e";
import { T3_HOME } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

// AI-assisted; verified with the tests and tools listed above.
test("A recovery read failure settles the turn after the final start attempt", WEB, async ({ t3, app, screen }) => {
  const project = await t3.project("recovery-demo", { "README.md": "Synthetic recovery failure reproduction.\n" });
  await t3.thread({ project, title: "Recovery failure demo", messages: ["Prepare a short reply"], model: "showcase" });
  const db = `${T3_HOME}/userdata/statev2.sqlite`;
  const sql = (query: string) => execFileSync("sqlite3", ["-cmd", ".timeout 5000", "-json", db, query], { encoding: "utf8" }).trim();
  const [{ thread_id: threadId }] = JSON.parse(sql("SELECT thread_id FROM orchestration_v2_projection_threads WHERE json_extract(payload_json, '$.title') = 'Recovery failure demo'"));
  await t3.open();
  await screen.getByTestId("sidebar-row-card").first().waitFor({ state: "visible", timeout: 120_000 });
  await screen.getByTestId("sidebar-row-card").first().press("Enter");
  sql(`CREATE TRIGGER corrupt_recovery_on_followup AFTER INSERT ON orchestration_v2_projection_messages
    WHEN NEW.thread_id = '${threadId}' AND NEW.role = 'user'
    BEGIN
      INSERT INTO orchestration_v2_projection_turn_items
        (turn_item_id, thread_id, run_id, node_id, provider_thread_id, provider_turn_id, type, status, ordinal, updated_at, payload_json)
      VALUES ('synthetic-unreadable-recovery', NEW.thread_id, NULL, NULL, NULL, NULL,
        'command_execution', 'running', -1, NEW.created_at, '{"obsolete":true}');
    END;`);
  await screen.getByRole("textbox").fill("Continue after the stored recovery state becomes unreadable");
  await screen.getByRole("textbox").press("Enter");
  for (let read = 0; read < 120; read++) {
    const rows = JSON.parse(sql(`SELECT status, attempt_count FROM orchestration_v2_effect_outbox WHERE thread_id = '${threadId}' AND effect_type = 'provider-turn.start' ORDER BY created_at DESC LIMIT 1`));
    if (rows[0]?.attempt_count >= 5 && ["failed", "succeeded"].includes(rows[0]?.status)) break;
    if (read === 119) throw new Error("The final start attempt did not finish");
    await setTimeout(1000);
  }
  try {
    await expect(screen.getByText("Couldn't read thread state", { exact: true })).toBeVisible();
    await expect(screen.getByRole("button", "Stop generation")).toBeHidden();
  } finally {
    await app.screenshot("final-start-attempt");
  }
});
