import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { test } from "bun:test";

import type { AgentChatResultResponse, ChatMessage } from "../types";
import {
  messageHasTerminalResponse,
  messageNeedsTerminalDiagnostics,
  terminalAuthorityMessageFromTurnResult,
  terminalMessagePatchFromTurnResult,
  traceHasTerminalResponse,
  turnResultIsTerminalForMerge,
  turnResultStatusForMerge
} from "./workbench-message-projection-runtime";

const storeDir = path.dirname(fileURLToPath(import.meta.url));

function agentMessage(input: Partial<ChatMessage> & Pick<ChatMessage, "id" | "traceId">): ChatMessage {
  return {
    role: "agent",
    title: "Code Agent",
    text: "",
    status: "running",
    createdAt: "2026-07-01T00:00:00.000Z",
    ...input
  };
}

function turnResult(input: AgentChatResultResponse): AgentChatResultResponse {
  return input;
}

function record(value: unknown): Record<string, unknown> | null {
  return value && typeof value === "object" ? value as Record<string, unknown> : null;
}

test("turn result merge keeps completed result without final response unsealed", () => {
  const traceId = "trc_frontend_terminal_without_final";
  const message = agentMessage({ id: "msg_frontend_terminal_without_final", status: "running", traceId });
  const result = turnResult({
    traceId,
    status: "completed",
    terminal: true,
    terminalSealBlocked: true,
    waitingFor: "final_response",
    runnerTrace: {
      traceId,
      events: [
        { seq: 1, type: "assistant_message", status: "running", message: "progress only" },
        { seq: 2, type: "terminal_status", terminal: true, terminalStatus: "completed" }
      ]
    }
  });

  assert.equal(turnResultStatusForMerge(result), "running");
  assert.equal(turnResultIsTerminalForMerge(result), false);
  assert.equal(terminalMessagePatchFromTurnResult(message, result), null);
});

test("turn result merge seals completed result with final response", () => {
  const traceId = "trc_frontend_terminal_with_final";
  const message = agentMessage({ id: "msg_frontend_terminal_with_final", status: "running", traceId });
  const result = turnResult({
    traceId,
    status: "completed",
    terminal: true,
    finalResponse: { text: "final answer" }
  });

  const patch = terminalMessagePatchFromTurnResult(message, result);
  assert.equal(turnResultStatusForMerge(result), "completed");
  assert.equal(turnResultIsTerminalForMerge(result), true);
  assert.equal(patch?.status, "completed");
  assert.equal(record(patch?.finalResponse)?.text, "final answer");
  assert.equal(patch?.text, "final answer");
});

test("turn authority creates terminal agent message when projection row is missing", () => {
  const traceId = "trc_frontend_terminal_missing_row";
  const message = terminalAuthorityMessageFromTurnResult(turnResult({
    traceId,
    sessionId: "ses_terminal_missing_row",
    threadId: "thr_terminal_missing_row",
    status: "completed",
    terminal: true,
    finalResponse: { text: "final answer without session message row" },
    startedAt: "2026-07-02T13:18:40.000Z",
    finishedAt: "2026-07-02T13:18:47.000Z"
  }));

  assert.equal(message?.role, "agent");
  assert.equal(message?.traceId, traceId);
  assert.equal(message?.sessionId, "ses_terminal_missing_row");
  assert.equal(message?.status, "completed");
  assert.equal(message?.text, "final answer without session message row");
  assert.equal(record(message?.finalResponse)?.source, "turn-result");
  assert.equal(messageHasTerminalResponse(message), true);
});

test("turn result merge seals failed result with failure final response", () => {
  const traceId = "trc_frontend_terminal_failed_with_body";
  const message = agentMessage({ id: "msg_frontend_terminal_failed_with_body", status: "running", traceId });
  const result = turnResult({
    traceId,
    status: "failed",
    terminal: true,
    finalResponse: { text: "Workbench terminal failed: provider-stream-disconnected", status: "failed" }
  });

  const patch = terminalMessagePatchFromTurnResult(message, result);
  assert.equal(turnResultStatusForMerge(result), "failed");
  assert.equal(turnResultIsTerminalForMerge(result), true);
  assert.equal(patch?.status, "failed");
  assert.equal(record(patch?.finalResponse)?.status, "failed");
  assert.equal(record(patch?.finalResponse)?.text, "Workbench terminal failed: provider-stream-disconnected");
  assert.equal(patch?.text, "Workbench terminal failed: provider-stream-disconnected");
});

test("terminal response helpers treat failed body as sealed authority", () => {
  const traceId = "trc_frontend_terminal_failed_helper";
  const sealed = agentMessage({ id: "msg_frontend_terminal_failed_helper", status: "failed", traceId, text: "Workbench terminal failed: provider-stream-disconnected", finalResponse: { text: "Workbench terminal failed: provider-stream-disconnected" } });
  const unsealed = agentMessage({ id: "msg_frontend_terminal_failed_helper_unsealed", status: "failed", traceId });

  assert.equal(messageHasTerminalResponse(sealed), true);
  assert.equal(messageNeedsTerminalDiagnostics(sealed), false);
  assert.equal(traceHasTerminalResponse(traceId, [sealed]), true);

  assert.equal(messageHasTerminalResponse(unsealed), false);
  assert.equal(messageNeedsTerminalDiagnostics(unsealed), true);
  assert.equal(traceHasTerminalResponse(traceId, [unsealed]), false);
});

test("workbench active state uses Kafka SSE without HTTP session or terminal projection", () => {
  const source = fs.readFileSync(path.join(storeDir, "workbench.ts"), "utf8");
  const submitBlock = source.slice(source.indexOf("async function submitMessage"), source.indexOf("async function cancelAgentMessage"));
  const traceDetailApplyBlock = source.slice(source.indexOf("function applyTraceDetailEventsResult"), source.indexOf("function setProviderProfile"));
  const traceDetailReadBlock = source.slice(source.indexOf("async function readTraceEventsForExplicitDetailPages"), source.indexOf("async function fetchTraceDetailEventsPage"));
  const loadBlock = source.slice(source.indexOf("function loadWorkbenchSession"), source.indexOf("function reattachRestoredActiveTrace"));

  assert.match(source, /const traceDetailReadSingleflight = createKeyedSingleflight<void>\(\)/u);
  assert.match(source, /traceDetailReadSingleflight\.run/u);
  assert.doesNotMatch(source, new RegExp(["refresh", "RealtimeSessionMessages"].join(""), "u"));
  assert.doesNotMatch(source, /function applyRealtimeTurnSnapshot|function applyRealtimeMessageSnapshot|function applyRealtimeTraceSnapshot/u);
  assert.match(source.slice(source.indexOf("function handleWorkbenchProjectionSignal"), source.indexOf("function applyTraceSnapshot")), /restartRealtime\("cross-tab-session-projection", true\)/u);
  assert.doesNotMatch(source, /scheduleActiveTraceSyncReplay|refreshActiveTraceFromSyncReplay/u);
  assert.match(traceDetailReadBlock, /traceAuthorityById\.value\[traceId\] \?\? message\.runnerTrace/u);
  assert.match(traceDetailReadBlock, /traceEventsDetailReadDecision\(traceId, afterProjectedSeq, message, options\)/u);
  assert.match(traceDetailReadBlock, /trace_events_auto_read_skip/u);
  assert.doesNotMatch(source, /fetchSessionDetailPage|fetchSessionMessagesPage/u);
  assert.doesNotMatch(loadBlock, /fetchSession\(|fetchSessionMessages\(/u);
  assert.match(loadBlock, /workbenchSessionNavigationSeed\(id/u);
  assert.match(source, /firstUserMessagePreview: firstNonEmptyString\(source\?\.firstUserMessagePreview\)/u);
  assert.match(source, /lastUserMessageAt: firstNonEmptyString\(source\?\.lastUserMessageAt\)/u);
  assert.doesNotMatch(submitBlock, /applyTurnStatusSnapshot|completeTrace/u);
  assert.doesNotMatch(traceDetailApplyBlock, /rememberTurnStatus|projectTurnAuthorityToMessages|chatPending\.value\s*=\s*false/u);
  assert.doesNotMatch(source, /sealRestoredActiveTurnMessages|messageNeedsRestoredTurnSeal|refreshSessionStatusAuthority|readTerminalTraceDetailGaps/u);
});

test("workbench active SSE path only recovers by reconnecting the events cursor", () => {
  const source = fs.readFileSync(path.join(storeDir, "workbench.ts"), "utf8");
  const activeBlocks = [
    source.slice(source.indexOf("async function submitMessage"), source.indexOf("async function cancelAgentMessage")),
    source.slice(source.indexOf("function reattachTrace"), source.indexOf("function restartRealtime"))
  ].join("\n");

  assert.doesNotMatch(activeBlocks, /refreshWorkbenchSyncReplay|scheduleActiveTraceSyncReplay|setTimeout|setInterval/u);
  assert.match(source.slice(source.indexOf("function executeRealtimeRecoveryStep"), source.indexOf("function stopRealtime")), /case "events-reconnect"[\s\S]*restartRealtime\(step\.reason, true\)/u);
  assert.doesNotMatch(source, /refreshWorkbenchSyncReplay|fetchSyncReplay|\/v1\/workbench\/sync/u);
});

test("cross page projection signal reconnects the durable projection SSE", () => {
  const source = fs.readFileSync(path.join(storeDir, "workbench.ts"), "utf8");
  const signalBlock = source.slice(source.indexOf("function handleWorkbenchProjectionSignal"), source.indexOf("function applyTraceSnapshot"));

  assert.match(signalBlock, /restartRealtime\("cross-tab-session-projection", true\)/u);
  assert.doesNotMatch(signalBlock, /messages\.value\.some/u);
  assert.doesNotMatch(signalBlock, /refreshCrossTabSessionFromSyncReplay|refreshWorkbenchSyncReplay/u);
});
