import { describe, expect, test } from "bun:test";
import { MetricsRegistry } from "../src/metrics.ts";
import {
  createOperationTracer,
  createOtlpLogExporter,
  createPerformanceProfiler,
  createPrometheusPushExporter,
  createSentryCompatibleReporter,
  createZipkinExporter,
  renderPrometheus,
} from "../src/integrations.ts";

describe("observability integrations", () => {
  test("records every framework operation kind and failures", async () => {
    const spans: any[] = [];
    const tracer = createOperationTracer({
      exporter: {
        export: (records) => {
          spans.push(...records);
        },
      },
      now: (() => {
        let time = 0;
        return () => (time += 5);
      })(),
    });
    for (const kind of [
      "database",
      "cache",
      "queue",
      "realtime",
      "server-action",
      "application",
    ] as const)
      expect(await tracer.span(kind, `${kind}.work`, async () => kind)).toBe(kind);
    await expect(
      tracer.span("application", "failure", async () => {
        throw new Error("boom");
      }),
    ).rejects.toThrow("boom");
    expect(spans.map((span) => span.attributes["wrnexus.span.kind"])).toContain("database");
    expect(spans.at(-1).status).toBe("error");
  });

  test("exports Prometheus, Zipkin/Jaeger, OTLP logs and Sentry-compatible errors", async () => {
    const requests: Array<{ url: string; init?: RequestInit }> = [];
    const send = (async (url: URL | RequestInfo, init?: RequestInit) => {
      requests.push({ url: String(url), init });
      return new Response(null, { status: 200 });
    }) as typeof fetch;
    const registry = new MetricsRegistry(() => 10);
    registry.increment("http.requests", 1, { route: "/" });
    expect(renderPrometheus(registry.snapshot())).toContain('http_requests{route="/"} 1');
    await createPrometheusPushExporter("https://prom.test", { fetch: send }).export(
      registry.snapshot(),
    );
    const span = {
      name: "db",
      traceId: "1".repeat(32),
      spanId: "2".repeat(16),
      sampled: true,
      startTime: 1,
      endTime: 2,
      durationMs: 1,
      status: "ok" as const,
      attributes: {},
    };
    await createZipkinExporter("https://zipkin.test", { fetch: send }).export([span]);
    await createOtlpLogExporter("https://otlp.test", { fetch: send }).export([
      {
        timestamp: new Date(0).toISOString(),
        level: "info",
        message: "ready",
        service: "app",
        attributes: {},
      },
    ]);
    expect(
      await createSentryCompatibleReporter("https://sentry.test", { fetch: send }).capture(
        new Error("bad"),
      ),
    ).toHaveLength(32);
    expect(requests).toHaveLength(4);
  });

  test("profiles application operations", async () => {
    const profiles: any[] = [];
    let time = 0;
    const profile = createPerformanceProfiler({
      now: () => (time += 4),
      onProfile: (value) => profiles.push(value),
    });
    expect(await profile("render", () => "ok")).toBe("ok");
    expect(profiles[0]).toMatchObject({ name: "render", durationMs: 4 });
  });

  test("operation exporter failures reach diagnostics without recursive telemetry", async () => {
    const diagnostics: string[] = [];
    const tracer = createOperationTracer({
      exporter: { export: () => Promise.reject(new Error("offline")) },
      diagnostic: (message) => diagnostics.push(message),
    });
    await expect(tracer.span("database", "query", async () => 1)).resolves.toBe(1);
    expect(diagnostics).toEqual(["operation span export failed"]);
  });
});
