// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-19-p1-agentrun-incremental-cursor.
// Responsibility: Workbench projection writer/finalizer durable facts regression tests.

import assert from "node:assert/strict";
import { test } from "bun:test";

import { createCloudRuntimeStore } from "../db/runtime-store.ts";
import { writeWorkbenchProjectionEvent, writeWorkbenchProjectionSession } from "./workbench-projection-writer.ts";

test("workbench projection writer commits terminal owner evidence as sealed durable facts", async () => {
  const factWrites = [];
  const accessWrites = [];
  const runtimeStore = {
    async writeWorkbenchFacts(params, requestMeta) {
      factWrites.push({ params, requestMeta });
      return { written: true, facts: params.facts };
    }
  };
  const accessController = {
    async recordAgentSessionOwner(input) {
      accessWrites.push(input);
      return {
        id: input.sessionId,
        projectId: input.projectId,
        ownerUserId: input.ownerUserId,
        conversationId: input.conversationId,
        threadId: input.threadId,
        lastTraceId: input.traceId,
        status: input.status,
        session: input.session,
        updatedAt: "2026-06-20T11:00:00.000Z"
      };
    }
  };

  await writeWorkbenchProjectionSession({
    accessController,
    runtimeStore,
    traceId: "trc_writer_terminal",
    ownerUserId: "usr_writer",
    ownerRole: "user",
    sessionId: "ses_writer_terminal",
    projectId: "prj_writer",
    conversationId: "cnv_writer_terminal",
    threadId: "thread-writer-terminal",
    status: "completed",
    payload: {
      traceId: "trc_writer_terminal",
      status: "completed",
      assistantText: "final answer",
      runnerTrace: {
        traceId: "trc_writer_terminal",
        status: "completed",
        startedAt: "2026-06-20T10:59:30.000Z",
        lastEventAt: "2026-06-20T11:00:00.000Z",
        finishedAt: "2026-06-20T11:00:00.000Z",
        elapsedMs: 30000
      },
      agentRun: { runId: "run_writer_terminal", commandId: "cmd_writer_terminal", lastSeq: 42 },
      updatedAt: "2026-06-20T11:00:00.000Z"
    },
    session: {
      sessionStatus: "completed",
      messages: [
        { messageId: "msg_writer_user", role: "user", text: "question", status: "sent", turnId: "trc_writer_terminal", traceId: "trc_writer_terminal" },
        { messageId: "msg_writer_agent", role: "agent", text: "final answer", status: "completed", turnId: "trc_writer_terminal", traceId: "trc_writer_terminal" }
      ],
      finalResponse: { text: "final answer", status: "completed", traceId: "trc_writer_terminal" }
    }
  });

  assert.equal(accessWrites.length, 1);
  assert.equal(factWrites.length, 1);
  const facts = factWrites[0].params.facts;
  assert.equal(facts.sessions[0].status, "completed");
  assert.equal(facts.sessions[0].sealed, true);
  assert.equal(facts.messages.length, 2);
  assert.equal(facts.messages.find((message) => message.messageId === "msg_writer_agent").sealed, true);
  assert.equal(facts.parts.some((part) => part.messageId === "msg_writer_agent" && part.text === "final answer" && part.sealed === true), true);
  assert.equal(facts.turns[0].terminal, true);
  assert.equal(facts.turns[0].sealed, true);
  assert.equal(facts.turns[0].finalResponse.text, "final answer");
  assert.equal(facts.messages.find((message) => message.messageId === "msg_writer_agent").lastEventAt, "2026-06-20T11:00:00.000Z");
  assert.equal(facts.messages.find((message) => message.messageId === "msg_writer_agent").durationMs, 30000);
  assert.equal(facts.turns[0].startedAt, "2026-06-20T10:59:30.000Z");
  assert.equal(facts.turns[0].durationMs, 30000);
  assert.equal(facts.checkpoints[0].projectionStatus, "caught_up");
  assert.equal(facts.checkpoints[0].sealed, true);
  assert.equal(facts.checkpoints[0].timing.finishedAt, "2026-06-20T11:00:00.000Z");
});

test("workbench projection event commit writes trace event and checkpoint facts", async () => {
  const factWrites = [];
  const baseStore = createCloudRuntimeStore({ now: () => "2026-06-20T11:00:00.000Z" });
  const runtimeStore = {
    allocateWorkbenchProjectedSeq(params, requestMeta) {
      return baseStore.allocateWorkbenchProjectedSeq(params, requestMeta);
    },
    queryWorkbenchFacts(params) {
      return baseStore.queryWorkbenchFacts(params);
    },
    async writeWorkbenchFacts(params, requestMeta) {
      const result = baseStore.writeWorkbenchFacts(params, requestMeta);
      factWrites.push({ params, requestMeta });
      return result;
    }
  };

  await writeWorkbenchProjectionEvent({
    runtimeStore,
    event: {
      traceId: "trc_writer_event",
      sessionId: "ses_writer_event",
      seq: 7,
      sourceSeq: 70,
      type: "backend",
      status: "running",
      label: "agentrun:backend:running",
      runId: "run_writer_event",
      commandId: "cmd_writer_event",
      startedAt: "2026-06-20T11:00:30.000Z",
      durationMs: 3000,
      createdAt: "2026-06-20T11:01:00.000Z"
    }
  });
  await writeWorkbenchProjectionEvent({
    runtimeStore,
    event: {
      traceId: "trc_writer_event",
      sessionId: "ses_writer_event",
      seq: 8,
      sourceSeq: 71,
      type: "result",
      status: "completed",
      label: "agentrun:result:completed",
      terminal: true,
      runId: "run_writer_event",
      commandId: "cmd_writer_event",
      createdAt: "2026-06-20T11:02:00.000Z"
    }
  });

  assert.equal(factWrites.length, 2);
  assert.equal(factWrites[0].params.facts.traceEvents[0].projectedSeq, 1);
  assert.equal(factWrites[0].params.facts.checkpoints[0].projectionStatus, "projecting");
  assert.equal(factWrites[0].params.facts.checkpoints[0].startedAt, "2026-06-20T11:00:30.000Z");
  assert.equal(factWrites[0].params.facts.checkpoints[0].lastEventAt, "2026-06-20T11:01:00.000Z");
  assert.equal(factWrites[0].params.facts.checkpoints[0].durationMs, null);
  assert.equal(factWrites[0].params.facts.checkpoints[0].timing.durationMs, null);
  assert.equal(factWrites[1].params.facts.sessions[0].status, "completed");
  assert.equal(factWrites[1].params.facts.traceEvents[0].sealed, true);
  assert.equal(factWrites[1].params.facts.traceEvents[0].projectedSeq, 2);
  assert.equal(factWrites[1].params.facts.checkpoints[0].projectionStatus, "caught_up");
  assert.equal(factWrites[1].params.facts.checkpoints[0].durationMs, 90000);
  assert.equal(factWrites[1].params.facts.checkpoints[0].finishedAt, "2026-06-20T11:02:00.000Z");
});

test("workbench projection event writer allocates projectedSeq idempotently by source event identity", async () => {
  const runtimeStore = createCloudRuntimeStore({ now: () => "2026-06-20T12:00:00.000Z" });

  const repeatedSourceEvent = {
    traceId: "trc_writer_idempotent",
    sessionId: "ses_writer_idempotent",
    seq: 1,
    source: "agentrun",
    sourceSeq: 70,
    type: "backend",
    status: "running",
    label: "agentrun:backend:running",
    runId: "run_writer_idempotent",
    commandId: "cmd_writer_idempotent",
    startedAt: "2026-06-20T12:00:01.000Z",
    createdAt: "2026-06-20T12:00:01.000Z"
  };
  await writeWorkbenchProjectionEvent({ runtimeStore, event: repeatedSourceEvent });
  await writeWorkbenchProjectionEvent({
    runtimeStore,
    event: {
      ...repeatedSourceEvent,
      seq: 1,
      sourceSeq: 71,
      label: "agentrun:backend:runner-ready",
      startedAt: null,
      durationMs: 999,
      createdAt: "2026-06-20T12:00:02.000Z"
    }
  });
  await writeWorkbenchProjectionEvent({ runtimeStore, event: repeatedSourceEvent });

  const loaded = runtimeStore.queryWorkbenchFacts({ traceId: "trc_writer_idempotent", families: ["traceEvents", "checkpoints"], limit: 10 });
  assert.deepEqual(loaded.facts.traceEvents.map((event) => event.projectedSeq), [1, 2]);
  assert.deepEqual(loaded.facts.traceEvents.map((event) => event.sourceSeq), [70, 71]);
  assert.equal(loaded.facts.checkpoints[0].projectedSeq, 2);
  assert.equal(loaded.facts.checkpoints[0].startedAt, "2026-06-20T12:00:01.000Z");
  assert.equal(loaded.facts.checkpoints[0].lastEventAt, "2026-06-20T12:00:02.000Z");
  assert.equal(loaded.facts.checkpoints[0].durationMs, null);
});
