import { writeFileSync } from "node:fs";
import { DateTime } from "effect";
import {
  MessageId,
  PlanId,
  RunId,
  ThreadId,
  TurnItemId,
  type OrchestrationV2PlanArtifact,
  type OrchestrationV2ProjectedTurnItem,
  type OrchestrationV2TurnItem,
} from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";
import {
  deriveTimelineEntriesFromVisibleTurnItems,
  deriveTimelineEntriesFromVisibleTurnItemsWithState,
  type TimelineEntry,
} from "./session-logic";
import {
  deriveMessagesTimelineRows,
  deriveMessagesTimelineRowsWithState,
} from "./components/chat/MessagesTimeline.logic";

function rng(seed: number) {
  let s = seed >>> 0;
  return () => {
    s = (s * 1664525 + 1013904223) >>> 0;
    return s / 2 ** 32;
  };
}

const threadId = ThreadId.make("fuzz-thread");
const runId = RunId.make("live-run");
const historyRunId = RunId.make("history-run");
const time = (second: number) => new Date(Date.UTC(2026, 8, 4, 0, 0, second)).toISOString();

function build(seed: number) {
  const r = rng(seed);
  const pick = <T>(xs: readonly T[]) => xs[Math.floor(r() * xs.length)]!;
  let ordinal = 0;
  const base = (id: string, run: RunId) => {
    const second = ordinal++;
    return {
      id: TurnItemId.make(id),
      threadId,
      runId: run,
      nodeId: null,
      providerThreadId: null,
      providerTurnId: null,
      nativeItemRef: null,
      parentItemId: null,
      ordinal: second,
      status: "completed" as const,
      title: null,
      startedAt: DateTime.makeUnsafe(time(second)),
      completedAt: DateTime.makeUnsafe(time(second)),
      updatedAt: DateTime.makeUnsafe(time(second)),
    };
  };
  const items: OrchestrationV2TurnItem[] = [
    {
      ...base("h-user", historyRunId),
      type: "user_message",
      messageId: MessageId.make("h-user"),
      text: "Inspect",
      attachments: [],
      inputIntent: "turn_start",
      createdBy: "user",
      creationSource: "web",
    },
    {
      ...base("h-reason", historyRunId),
      type: "reasoning",
      text: "old thought",
      streaming: false,
    },
    {
      ...base("h-assistant", historyRunId),
      type: "assistant_message",
      messageId: MessageId.make("h-assistant"),
      text: "Done",
      streaming: false,
    },
    {
      ...base("l-user", runId),
      type: "user_message",
      messageId: MessageId.make("l-user"),
      text: "Continue",
      attachments: [],
      inputIntent: pick(["turn_start", "turn_start"] as const),
      createdBy: "user",
      creationSource: "web",
    },
  ];
  const streamingIds: string[] = [];
  const plans: OrchestrationV2PlanArtifact[] = [];
  const count = 1 + Math.floor(r() * 7);
  for (let i = 0; i < count; i += 1) {
    const kind = pick([
      "reason-stream",
      "reason-stream",
      "reason-done",
      "tool-done",
      "tool-running",
      "tool-failed",
      "assistant-stream",
      "assistant-done",
      "plan-stream",
      "compaction-running",
      "compaction-done",
      "steer",
    ] as const);
    const id = `i${i}`;
    switch (kind) {
      case "reason-stream":
        streamingIds.push(id);
        items.push({
          ...base(id, runId),
          status: "running",
          completedAt: null,
          type: "reasoning",
          text: pick(["", " ", "a"]),
          streaming: true,
        });
        break;
      case "reason-done":
        items.push({ ...base(id, runId), type: "reasoning", text: "done", streaming: false });
        break;
      case "tool-done":
      case "tool-running":
      case "tool-failed":
        items.push({
          ...base(id, runId),
          ...(kind === "tool-running"
            ? { status: "running" as const, completedAt: null }
            : kind === "tool-failed"
              ? { status: "failed" as const }
              : {}),
          type: "command_execution",
          input: "vp test",
          output: "",
          exitCode: kind === "tool-failed" ? 1 : 0,
        } as OrchestrationV2TurnItem);
        break;
      case "assistant-stream":
        streamingIds.push(id);
        items.push({
          ...base(id, runId),
          status: "running",
          completedAt: null,
          type: "assistant_message",
          messageId: MessageId.make(id),
          text: "x",
          streaming: true,
        });
        break;
      case "assistant-done":
        items.push({
          ...base(id, runId),
          type: "assistant_message",
          messageId: MessageId.make(id),
          text: "said",
          streaming: false,
        });
        break;
      case "plan-stream":
        streamingIds.push(id);
        plans.push({
          id: PlanId.make(`plan-${id}`),
          threadId,
          runId,
          nodeId: null,
          kind: "proposed_plan",
          status: "active",
          markdown: "",
        } as unknown as OrchestrationV2PlanArtifact);
        items.push({
          ...base(id, runId),
          status: "running",
          completedAt: null,
          type: "proposed_plan",
          planId: PlanId.make(`plan-${id}`),
          markdown: "",
          streaming: true,
        });
        break;
      case "compaction-running":
      case "compaction-done":
        items.push({
          ...base(id, runId),
          ...(kind === "compaction-running" ? { status: "running" as const, completedAt: null } : {}),
          type: "compaction",
          driver: null,
        } as OrchestrationV2TurnItem);
        break;
      case "steer":
        items.push({
          ...base(id, runId),
          type: "user_message",
          messageId: MessageId.make(id),
          text: "steer",
          attachments: [],
          inputIntent: "steer",
          createdBy: "user",
          creationSource: "web",
        });
        break;
    }
  }
  const visibleTurnItems: OrchestrationV2ProjectedTurnItem[] = items.map((item, position) => ({
    position,
    visibility: "local",
    sourceThreadId: threadId,
    sourceItemId: item.id,
    item,
  }));
  const running = r() < 0.8;
  const expandAll = r() < 0.4;
  const expandRuns = r() < 0.3;
  return { r, pick, visibleTurnItems, streamingIds, plans, running, expandAll, expandRuns };
}

describe("review-b differential fuzz", () => {
  it("fast paths match full derivation", () => {
    let fastHits = 0;
    let entryHits = 0;
    let total = 0;
    for (let seed = 1; seed <= 3000; seed += 1) {
      const c = build(seed);
      if (c.streamingIds.length === 0) continue;
      const rowsInput = (
        timelineEntries: ReadonlyArray<TimelineEntry>,
        groupIds: ReadonlySet<string>,
      ) => ({
        timelineEntries,
        latestRun: {
          runId,
          status: c.running ? ("running" as const) : ("completed" as const),
          startedAt: time(3),
          completedAt: c.running ? null : time(59),
        },
        runningRunId: c.running ? runId : null,
        isWorking: c.running,
        activeTurnStartedAt: time(3),
        turnDiffSummaries: [],
        supportsConversationRollback: true,
        expandedWorkGroupIds: groupIds,
        ...(c.expandRuns ? { expandedRunIds: new Set([runId, historyRunId]) } : {}),
      });
      let items = c.visibleTurnItems;
      let plans = c.plans;
      let input = { visibleTurnItems: items, optimisticMessages: [], plans };
      let entries = deriveTimelineEntriesFromVisibleTurnItemsWithState(input);
      const groupIdsOf = (rows: ReturnType<typeof deriveMessagesTimelineRows>) =>
        new Set(
          c.expandAll
            ? rows.flatMap((row) =>
                row.kind === "work-live" || row.kind === "work-toggle" || row.kind === "thinking"
                  ? row.groupId
                    ? [row.groupId]
                    : []
                  : [],
              )
            : [],
        );
      let groupIds = groupIdsOf(deriveMessagesTimelineRows(rowsInput(entries.entries, new Set())));
      let rows = deriveMessagesTimelineRowsWithState(rowsInput(entries.entries, groupIds));
      for (let step = 0; step < 8; step += 1) {
        const n = c.r() < 0.2 ? 2 : 1;
        const targets = new Set(Array.from({ length: n }, () => c.pick(c.streamingIds)));
        const finish = c.r() < 0.1;
        items = items.map((row) => {
          if (!targets.has(row.item.id)) return row;
          const updatedAt = DateTime.makeUnsafe(time(40 + step));
          const text = c.pick(["", " ", "\n", "abc", "abc def", "  x  "]);
          const it = row.item;
          const done = finish
            ? { status: "completed" as const, streaming: false, completedAt: updatedAt }
            : {};
          if (it.type === "reasoning" || it.type === "assistant_message") {
            if (!it.streaming) return row;
            return { ...row, item: { ...it, text: it.text + text, updatedAt, ...done } };
          }
          if (it.type === "proposed_plan") {
            if (!it.streaming) return row;
            return { ...row, item: { ...it, markdown: it.markdown + text, updatedAt, ...done } };
          }
          return row;
        });
        plans = plans.map((plan) =>
          c.r() < 0.5 ? ({ ...plan, markdown: `${plan.markdown}.` } as typeof plan) : plan,
        );
        input = { visibleTurnItems: items, optimisticMessages: [], plans };
        const previousEntries = entries;
        entries = deriveTimelineEntriesFromVisibleTurnItemsWithState(input, previousEntries);
        const full = deriveTimelineEntriesFromVisibleTurnItems(input);
        if (entries.entries.some((e, i) => e === previousEntries.entries[i])) entryHits += 1;
        expect(entries.entries, `entries seed ${seed} step ${step}`).toEqual(full);
        const previousRows = rows;
        rows = deriveMessagesTimelineRowsWithState(
          rowsInput(entries.entries, groupIds),
          previousRows,
        );
        const fullRows = deriveMessagesTimelineRows(rowsInput(full, groupIds));
        expect(rows.rows, `rows seed ${seed} step ${step}`).toEqual(fullRows);
        total += 1;
        if (rows.rows.some((row, index) => row === previousRows.rows[index])) fastHits += 1;
      }
      void groupIds;
      groupIds = new Set(groupIds);
    }
    writeFileSync("/tmp/issue-lead-24120-r3/review-b/fuzz-stats.txt", `checked ${total} steps, ${fastHits} with reused rows, entryReuse ${entryHits}\n`);
  });
});
