import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import type { Server } from "bun";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { OBSERVABILITY_REPORT_SCHEMA_VERSION, type ObservabilityReport, type ObservabilityReportQuery } from "@overdeck/report-contract";
import { Journal } from "../journal";
import { CollectorState } from "../state";
import { startServer } from "../server";

const TOKEN = "collector-report-token";

function report(query: ObservabilityReportQuery): ObservabilityReport {
  return {
    schemaVersion: OBSERVABILITY_REPORT_SCHEMA_VERSION,
    query,
    generatedAt: query.to,
    coverage: { status: "complete", from: query.from, to: query.to, generatedAt: query.to, sources: [], gaps: [] },
    sections: [{ kind: "activity", status: "complete", title: "Activity and source trust", metrics: [], series: [], categoryBreakdown: [], attention: [], sources: [], truncated: false, errors: [] }],
    filters: { projects: [], sources: [] },
  };
}

describe("GET /reports/observability", () => {
  let server: Server<undefined> | undefined;
  let dir: string;
  let state: CollectorState;

  beforeEach(() => {
    dir = mkdtempSync(join(tmpdir(), "collector-observability-report-route-"));
    state = new CollectorState(new Journal(join(dir, "items.jsonl")));
  });

  afterEach(() => {
    server?.stop(true);
    server = undefined;
    rmSync(dir, { recursive: true, force: true });
  });

  test("requires collector auth and passes the validated normalized query to the projection", async () => {
    let received: ObservabilityReportQuery | undefined;
    server = startServer({
      host: "127.0.0.1",
      port: 0,
      token: TOKEN,
      state,
      buildObservabilityReport: (query) => { received = query; return report(query); },
    });
    const from = "2026-08-15T12:00:00.000Z";
    const to = "2026-08-16T12:00:00.000Z";
    const url = `http://127.0.0.1:${server.port}/reports/observability?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&timezone=UTC&project=overdeck&project=deck&source=actions&severity=warn`;

    expect((await fetch(url)).status).toBe(401);
    const response = await fetch(url, { headers: { authorization: `Bearer ${TOKEN}` } });
    expect(response.status).toBe(200);
    expect(response.headers.get("content-type")).toContain("application/json");
    expect(received).toEqual({ from, to, timezone: "UTC", projects: ["overdeck", "deck"], sources: ["actions"], severity: "warn" });
    expect(await response.json()).toMatchObject({ schemaVersion: 1, query: received });
  });

  test("returns one owner-readable 400 without invoking the projection", async () => {
    let calls = 0;
    server = startServer({
      host: "127.0.0.1",
      port: 0,
      token: TOKEN,
      state,
      buildObservabilityReport: (query) => { calls += 1; return report(query); },
    });
    const response = await fetch(`http://127.0.0.1:${server.port}/reports/observability?timezone=Moon%2FBase`, { headers: { authorization: `Bearer ${TOKEN}` } });
    expect(response.status).toBe(400);
    expect(await response.json()).toEqual({ error: "invalid-report-query", message: "Report timezone is not recognized." });
    expect(calls).toBe(0);
  });

  test("returns a bounded unavailable response when projection fails", async () => {
    server = startServer({
      host: "127.0.0.1",
      port: 0,
      token: TOKEN,
      state,
      buildObservabilityReport: () => { throw new Error("/private/source/path"); },
    });
    const response = await fetch(`http://127.0.0.1:${server.port}/reports/observability?timezone=UTC`, { headers: { authorization: `Bearer ${TOKEN}` } });
    expect(response.status).toBe(503);
    expect(await response.json()).toEqual({ error: "observability-report-unavailable" });
  });
});
