import { describe, expect, test } from "bun:test";
import type { FactoryRunView } from "../adapters/factory";
import type { RequestRow } from "../requests/requests-store";
import type { ObservabilityReportQuery, RequestStoryV1 } from "@overdeck/report-contract";
import { buildExecutionReportSection } from "./execution-report";

const QUERY: ObservabilityReportQuery = {
  from: "2026-08-16T00:00:00.000Z",
  to: "2026-08-17T00:00:00.000Z",
  timezone: "UTC",
};

function request(overrides: Partial<RequestRow> = {}): RequestRow {
  return {
    id: "request-1",
    title: "Ship the reports page",
    project: "overdeck",
    state: "shipped",
    priority: "HIGH",
    origin: "owner",
    asked_at: "2026-08-16T01:00:00.000Z",
    original_body: "Ship the reports page",
    original_body_format: "plain_text",
    intake_source: "owner",
    intake_source_event_id: "request-1",
    updated_at: "2026-08-16T10:00:00.000Z",
    worker: "claude",
    detail: null,
    proof_url: "/reports",
    plan_ref: null,
    factory_run_id: "adw-linked",
    session_name: null,
    session_id: null,
    announced_at: null,
    receipt_trail: [
      { id: "landed", request_id: "request-1", at: "2026-08-16T08:00:00.000Z", kind: "landed", line: "Landed", meta: {} },
      { id: "deployed", request_id: "request-1", at: "2026-08-16T09:00:00.000Z", kind: "deployed", line: "Deployed", meta: {} },
    ],
    transition_trail: [],
    ...overrides,
  };
}

function story(requestId: string, linked = true): RequestStoryV1 {
  const at = "2026-08-16T08:00:00.000Z";
  const makeLink = (kind: RequestStoryV1["links"][number]["kind"], targetId: string, metadata: Record<string, unknown> = {}) => ({
    kind, targetSource: "exact-source", targetId, occurredAt: at, sourceId: "request-evidence", sourceEventId: `${kind}:${requestId}`, metadata,
  });
  return {
    schemaVersion: 1, requestId, title: "Ship the reports page", project: "overdeck", state: "shipped", priority: "HIGH",
    askedAt: "2026-08-16T01:00:00.000Z", updatedAt: "2026-08-16T10:00:00.000Z",
    originalRequest: { body: "Ship the reports page", format: "plain_text", source: "owner" }, events: [], attachments: [], coverage: [],
    links: linked ? [
      makeLink("factory_run", "adw-linked"),
      { ...makeLink("branch", "wt/reports", { submittedHead: "a".repeat(40) }), sourceEventId: "land-submitted" },
      { ...makeLink("land_submission", "ticket-1"), sourceEventId: "land-submitted" },
      { ...makeLink("land_submission", "ticket-1"), sourceEventId: "land-result" },
      { ...makeLink("commit", "b".repeat(40), { gateVerdict: "passed" }), sourceEventId: "land-result" },
      makeLink("deployment", "deploy-1", { release: "b".repeat(40), status: "deployed", readiness: "passed" }),
      { ...makeLink("proof", "proof-1", { release: "b".repeat(40), result: "passed" }), ownerUrl: "/reports#lineage" },
    ] : [],
  };
}

function run(overrides: Partial<FactoryRunView> = {}): FactoryRunView {
  return {
    adwId: "adw-linked",
    repo: "/workspace/overdeck",
    repoName: "overdeck",
    adwName: "plan-build",
    runSlug: "reports-slice",
    preset: null,
    request: "Ship the reports page",
    status: "success",
    engineer: null,
    startedAt: "2026-08-16T02:00:00.000Z",
    endedAt: "2026-08-16T03:00:00.000Z",
    totalTokens: 100,
    totalCost: null,
    phases: [{ phaseId: "phase-1", taskId: "task-1", seq: 1, name: "Build", kind: "build", owner: "agent", description: null, status: "completed", attempt: 2, retries: 1, error: null, startedAt: "2026-08-16T02:00:00.000Z", endedAt: "2026-08-16T03:00:00.000Z", durationMs: 3_600_000, tokens: 100, spend: 0 }],
    events: [],
    attempts: [{ attemptId: "attempt-1", phaseId: "phase-1", agent: "coder", sessionId: "session-1", command: null, systemPrompt: null, userPrompt: null, returncode: 0, signal: null, timedOut: false, timeoutKind: null, tokens: 100, error: null, stderrPath: null, host: "debian1", account: "claude11", model: "sonnet", usage: null, providerFailure: null, startedAt: "2026-08-16T02:00:00.000Z", lastOutputAt: null, endedAt: "2026-08-16T03:00:00.000Z", durationMs: 3_600_000, idleMs: null, toolCalls: [{ toolCallId: "tool-1", seq: 1, toolName: "Read", args: null, startedAt: null, endedAt: null, durationMs: 10, ok: true, resultExcerpt: null }] }],
    gates: [{ adwId: "adw-linked", phaseId: "phase-1", attempt: 1, gate: "tests", passed: true, violations: null, checks: null }],
    diffs: [{ adwId: "adw-linked", taskId: "task-1", attemptId: "attempt-1", linkage: "linked", phaseId: "phase-1", attempt: 1, files: [{ path: "reports.ts", status: "M", insertions: 10, deletions: 2 }], insertions: 10, deletions: 2, diffText: null, truncated: false, createdAt: "2026-08-16T03:00:00.000Z" }],
    processes: [],
    unavailableTables: [],
    decisions: [],
    ...overrides,
  };
}

describe("buildExecutionReportSection", () => {
  test("joins requests to runs only through the explicit factory run identity", () => {
    const section = buildExecutionReportSection(QUERY, {
      requests: [request(), request({ id: "request-unlinked", title: "Ship the reports page adw-linked", factory_run_id: "adw-linked", proof_url: "/legacy-proof" })],
      requestStories: [story("request-1"), story("request-unlinked", false)],
      factoryRuns: [run()],
    });

    expect(section.metrics.map((metric) => [metric.id, metric.value])).toEqual([
      ["requests", 2], ["shipped-requests", 2], ["factory-runs", 1], ["failed-runs", 0],
    ]);
    expect(section.requests.find((row) => row.id === "request-1")).toMatchObject({ correlation: "linked", factoryRunId: "adw-linked", delivery: { proofRecorded: true } });
    expect(section.requests.find((row) => row.id === "request-unlinked")).toMatchObject({ correlation: "unlinked" });
    expect(section.gaps.some((gap) => gap.reason.includes("No factory run was explicitly linked"))).toBe(true);
    expect(section.runs[0]).toMatchObject({ attempts: 1, retries: 1, failedGates: 0, toolCalls: 1, changedFiles: 1, models: ["sonnet"], accounts: ["claude11"], hosts: ["debian1"] });
  });

  test("keeps missing sources unavailable and never turns them into zero", () => {
    const section = buildExecutionReportSection(QUERY, {});
    expect(section.status).toBe("unavailable");
    expect(section.metrics.every((metric) => metric.value === null)).toBe(true);
    expect(section.gaps).toHaveLength(2);
  });

  test("uses explicit project and search filters without guessed correlation", () => {
    const section = buildExecutionReportSection({ ...QUERY, projects: ["other"], q: "reports" }, {
      requests: [request()],
      factoryRuns: [run()],
    });
    expect(section.metrics.map((metric) => metric.value)).toEqual([0, 0, 0, 0]);
    expect(section.requests).toEqual([]);
    expect(section.runs).toEqual([]);
  });
});
