import { describe, expect, test } from "bun:test";
import type { Panel } from "./schema";
import type { FactoryPanelData, FactoryRunView } from "./adapters/factory";
import { factoryRunDetail, factoryRunDetailResponse, publicCollectorDelta, summarizeFactoryPanel } from "./server";

function run(overrides: Partial<FactoryRunView> = {}): FactoryRunView {
  return {
    adwId: "run-1",
    repo: "/repo",
    repoName: "repo",
    adwName: "build",
    runSlug: "build-1",
    preset: null,
    request: "ship",
    status: "running",
    engineer: "agent",
    startedAt: "2026-08-12T00:00:00Z",
    endedAt: null,
    totalTokens: 1,
    totalCost: 0.1,
    phases: [{ phaseId: "phase-1", seq: 1, name: "build", kind: "agent", owner: "coder", description: null, status: "running", attempt: 0, retries: 0, error: null, startedAt: "2026-08-12T00:00:00Z", endedAt: null, durationMs: null, tokens: 1, spend: 0.1 }],
    events: [{ rowid: 1, eventId: "event-1", phaseId: "phase-1", parentId: null, type: "agent_output", name: null, payloadJson: "x".repeat(1_000_000), tokens: null, startedAt: null, endedAt: null }],
    attempts: [],
    gates: [],
    diffs: [],
    processes: [],
    unavailableTables: [],
    decisions: [{ decisionId: "decision-1", phase: "phase-1", question: "Continue?", options: [], freeText: false, context: "", status: "pending", answerValue: null, answerText: null, answeredBy: null, createdAt: "2026-08-12T00:00:00Z", answeredAt: null }],
    ...overrides,
  };
}

function panel(factoryRun = run()): Panel {
  return {
    id: "factory-runs",
    ts: "2026-08-12T00:00:00Z",
    data: { dbPath: "/state/sssf.db", dbPresent: true, runs: [factoryRun] } satisfies FactoryPanelData,
  };
}

describe("factory state projection", () => {
  test("keeps summary authority while removing trace arrays", () => {
    const source = panel();
    const summary = summarizeFactoryPanel(source);
    const data = summary.data as FactoryPanelData;

    expect(data.runs[0]).toMatchObject({ adwId: "run-1", status: "running", detailAvailable: true });
    expect(data.runs[0]?.decisions).toHaveLength(1);
    for (const field of ["phases", "events", "attempts", "gates", "diffs", "processes"] as const) {
      expect(data.runs[0]?.[field]).toEqual([]);
    }
    expect(JSON.stringify(summary).length).toBeLessThan(2_000);
    expect(((source.data as FactoryPanelData).runs[0]?.events[0]?.payloadJson ?? "").length).toBe(1_000_000);
  });

  test("projects SSE panels and preserves full per-run detail", () => {
    const source = panel();
    const delta = publicCollectorDelta({ type: "panel", panel: source });
    expect(delta.type).toBe("panel");
    if (delta.type === "panel") {
      expect(((delta.panel.data as FactoryPanelData).runs[0]?.events)).toEqual([]);
    }
    expect(factoryRunDetail(source, "run-1")?.events[0]?.payloadJson).toHaveLength(1_000_000);
    expect(factoryRunDetail(source, "missing")).toBeUndefined();
  });

  test("rejects oversized run detail responses", async () => {
    const response = factoryRunDetailResponse(run({ request: "x".repeat(8 * 1024 * 1024) }));
    expect(response.status).toBe(413);
    expect(await response.json()).toEqual({ error: "factory-run-detail-too-large" });
  });

  test("leaves unrelated panels and deltas unchanged", () => {
    const source: Panel = { id: "limits", ts: "2026-08-12T00:00:00Z", data: { ok: true } };
    expect(summarizeFactoryPanel(source)).toBe(source);
    const delta = { type: "item-resolved" as const, id: "item-1" };
    expect(publicCollectorDelta(delta)).toBe(delta);
  });
});
