import { configureTaskView, taskTraceSummaryFixtureResponse } from "../src/components/microservices/code-queue/src/task-view";
import { configureTaskOutput } from "../src/components/microservices/code-queue/src/task-output";
import { configureJudge } from "../src/components/microservices/code-queue/src/judge";
import type { OaTraceStepSummary } from "../src/components/microservices/code-queue/src/oa-events";
import type { JsonValue, PromptHistoryItem, QueueTask, QueuedStatusReason } from "../src/components/microservices/code-queue/src/types";

type JsonRecord = Record<string, unknown>;

function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void {
  if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}

function asRecord(value: unknown): JsonRecord | null {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value as JsonRecord : null;
}

function pageBySeq<T extends { seq: number }>(
  items: T[],
  _url: URL,
  _limit: number,
): { mode: "tail" | "after" | "before"; afterSeq: number; beforeSeq: number | null; nextAfterSeq: number; previousBeforeSeq: number | null; hasMore: boolean; hasBefore: boolean; chunk: T[] } {
  return {
    mode: "tail",
    afterSeq: 0,
    beforeSeq: null,
    nextAfterSeq: items.at(-1)?.seq ?? 0,
    previousBeforeSeq: null,
    hasMore: false,
    hasBefore: false,
    chunk: items,
  };
}

function configureFixtureTaskView(): void {
  configureTaskOutput({
    config: { maxInMemoryOutputRecords: 1000, outputArchiveDir: "/tmp/code-queue-trace-summary-contract/output" },
    allocateSeq: () => 1000,
    errorToJson: (error: unknown): JsonValue => error instanceof Error ? { message: error.message } : String(error),
    logger: () => undefined,
    markTaskDirty: () => undefined,
    nowIso: () => "2026-05-19T00:10:00.000Z",
    schedulePersistState: () => undefined,
  });
  configureJudge({
    config: {
      minimaxApiKey: "",
      minimaxApiBase: "",
      minimaxModel: "minimax-m1",
      judgeTimeoutMs: 1000,
      judgeRepairAttempts: 0,
      judgeMaxTokens: 1000,
    },
    logger: () => undefined,
    safePreview: (value: string, max = 300) => value.length > max ? `${value.slice(0, max)}...` : value,
    userPromptForDisplay: (prompt: string) => prompt,
    taskFullOutput: (task: QueueTask) => task.output,
    taskReferenceIds: (task: QueueTask) => task.referenceTaskIds,
    extractRecord: (value: unknown) => typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : null,
    extractString: (value: unknown, key: string) => {
      const record = typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : null;
      const item = record?.[key];
      return typeof item === "string" ? item : null;
    },
    promptLineCount: (text: string) => text.length > 0 ? text.split(/\r\n|\r|\n/u).length : 0,
    judgeFailRetryLimit: 3,
  });
  configureTaskView({
    config: { codexHome: "/tmp/code-queue-trace-summary-contract" },
    errorToJson: (error: unknown): JsonValue => error instanceof Error ? { message: error.message } : String(error),
    jsonResponse: (body: unknown, status = 200): Response => Response.json(body, { status }),
    logger: () => undefined,
    mergePromptHistory: (items: PromptHistoryItem[]) => items,
    nowIso: () => "2026-05-19T00:10:00.000Z",
    outputPromptHistory: () => [],
    pageBySeq,
    parseLimit: () => 100,
    parseSeqParam: () => null,
    queueIdOf: (task: QueueTask) => task.queueId,
    queuedStatusReason: (): QueuedStatusReason | null => null,
    queuedTaskPromptEditable: () => false,
    taskQueueEnteredAt: (task: QueueTask) => task.queueEnteredAt,
  });
}

function fixtureTask(): QueueTask {
  const at = "2026-05-19T00:00:00.000Z";
  return {
    id: "codex_trace_contract",
    queueId: "default",
    queueEnteredAt: at,
    prompt: "Trace summary contract fixture",
    basePrompt: "Trace summary contract fixture",
    referenceTaskIds: [],
    referenceInjection: null,
    providerId: "D601",
    cwd: "/workspace",
    model: "gpt-5.5",
    reasoningEffort: null,
    executionMode: "default",
    maxAttempts: 99,
    status: "running",
    createdAt: at,
    updatedAt: "2026-05-19T00:06:30.000Z",
    startedAt: at,
    finishedAt: null,
    readAt: null,
    currentAttempt: 2,
    currentMode: "retry",
    codexThreadId: "thread_trace_contract",
    activeTurnId: "turn_trace_contract",
    finalResponse: "",
    lastError: null,
    lastJudge: { decision: "retry", confidence: 1, reason: "attempt 1 asked for retry", source: "fallback" },
    judgeFailCount: 0,
    promptHistory: [],
    output: [
      { seq: 1, at, channel: "user", text: "Trace summary contract fixture", method: "enqueue" },
      { seq: 2, at: "2026-05-19T00:00:10.000Z", channel: "system", text: "attempt 1 / 99", method: "queue" },
      { seq: 3, at: "2026-05-19T00:01:00.000Z", channel: "command", text: "rg trace-summary src/components/microservices/code-queue/src", method: "item/started", itemId: "call-1" },
      { seq: 4, at: "2026-05-19T00:02:00.000Z", channel: "system", text: "judge=retry confidence=1 source=fallback: attempt 1 asked for retry", method: "judge" },
    ],
    events: [],
    attempts: [
      {
        index: 1,
        mode: "initial",
        startedAt: "2026-05-19T00:00:10.000Z",
        finishedAt: "2026-05-19T00:02:00.000Z",
        terminalStatus: "completed",
        transportClosedBeforeTerminal: false,
        appServerExitCode: 0,
        appServerSignal: null,
        error: null,
        finalResponse: "Attempt 1 response",
        finalResponsePreview: "Attempt 1 response",
        finalResponseChars: 18,
        stderrTail: "",
        judge: { decision: "retry", confidence: 1, reason: "attempt 1 asked for retry", source: "fallback" },
        judgeAt: "2026-05-19T00:02:00.000Z",
        judgeSeq: 4,
        outputStartSeq: 2,
        outputEndSeq: 4,
      },
    ],
    cancelRequested: false,
    nextPrompt: null,
    nextMode: null,
  };
}

function attempt2Steps(): OaTraceStepSummary[] {
  return [
    {
      eventSequence: 20,
      seq: 20,
      at: "2026-05-19T00:06:00.000Z",
      kind: "ran",
      title: "Run",
      status: "item/started",
      summaryLines: ["attempt 2 / 99", "pnpm test"],
      rawSeqs: [20],
      scopeId: "task:codex_trace_contract:attempt:2",
      attemptIndex: 2,
      source: "oa-event-flow",
    },
    {
      eventSequence: 21,
      seq: 21,
      at: "2026-05-19T00:06:20.000Z",
      kind: "explored",
      title: "Read",
      status: "item/completed",
      summaryLines: ["src/components/microservices/code-queue/src/task-view.ts"],
      rawSeqs: [21],
      scopeId: "task:codex_trace_contract:attempt:2",
      attemptIndex: 2,
      source: "oa-event-flow",
    },
  ];
}

export function runCodeQueueTraceSummaryContract(): JsonRecord {
  configureFixtureTaskView();
  const task = fixtureTask();
  const steps = attempt2Steps();
  const summary = taskTraceSummaryFixtureResponse(task, {
    stats: null,
    taskStats: null,
    allSteps: [
      {
        eventSequence: 1,
        seq: 1,
        at: "2026-05-19T00:00:10.000Z",
        kind: "message",
        title: "Assistant message",
        status: "item/completed",
        summaryLines: ["Attempt 1 judge complete"],
        rawSeqs: [4],
        scopeId: "task:codex_trace_contract",
        attemptIndex: null,
        source: "oa-event-flow",
      },
      ...steps,
    ],
    attemptSteps: new Map([[2, steps]]),
  }) as JsonRecord;
  const attempts = Array.isArray(summary.attempts) ? summary.attempts.map(asRecord).filter((item): item is JsonRecord => item !== null) : [];
  const attempt2 = attempts.find((attempt) => Number(attempt.index) === 2) ?? null;
  const taskStats = asRecord(summary.traceStats);
  const taskExecution = asRecord(summary.execution);
  const attempt2Stats = asRecord(attempt2?.traceStats);
  const attempt2Execution = asRecord(attempt2?.execution);

  assertCondition(summary.currentAttempt === 2, "summary must retain currentAttempt=2", summary);
  assertCondition(summary.statsSource === "raw-trace-fallback", "summary must distinguish raw trace fallback from empty STEP", summary);
  assertCondition(summary.traceStatsState === "degraded", "summary must mark OA stats sync degraded", summary);
  assertCondition(summary.traceStatsReason === "oa-event-flow-stats-unavailable-raw-trace-present", "summary must explain degraded OA sync", summary);
  assertCondition(taskStats?.source === "oa-event-flow" && taskStats?.sourceHint === "raw-trace-fallback", "summary must expose countable synthetic stats with source hint", taskStats ?? {});
  assertCondition(taskExecution?.statsSource === "oa-event-flow" && taskExecution?.traceStatsState === "degraded", "execution summary must stay countable while degraded", taskExecution ?? {});
  assertCondition(Number(summary.stepCount ?? 0) > 0, "summary fallback STEP count must be visible", summary);
  assertCondition(attempt2 !== null, "summary must materialize the latest running retry attempt", { attempts });
  assertCondition(Number(attempt2?.stepCount ?? 0) > 0, "attempt 2 must expose live fallback STEP count", attempt2 ?? {});
  assertCondition(attempt2Stats?.source === "oa-event-flow" && attempt2Stats?.sourceHint === "raw-trace-fallback", "attempt 2 fallback stats must remain countable", attempt2Stats ?? {});
  assertCondition(attempt2Execution?.statsSource === "oa-event-flow" && attempt2Execution?.traceStatsState === "degraded", "attempt 2 execution must be countable while degraded", attempt2Execution ?? {});

  return {
    ok: true,
    checks: [
      { name: "code-queue:trace-summary-latest-attempt-visible", ok: true },
      { name: "code-queue:trace-summary-raw-trace-step-fallback", ok: true },
    ],
    taskId: task.id,
    stepCount: summary.stepCount,
    statsSource: summary.statsSource,
    traceStatsState: summary.traceStatsState,
    attempt2StepCount: attempt2?.stepCount,
  };
}

if (import.meta.main) {
  process.stdout.write(`${JSON.stringify(runCodeQueueTraceSummaryContract(), null, 2)}\n`);
}
