import { describe, expect, test } from "bun:test";
import { createContext, HealthRegistry } from "@wrnexus/core";
import {
  createLivenessHandler,
  createOtlpMetricExporter,
  createOtlpTraceExporter,
  createReadinessHandler,
  createStructuredLogger,
  formatTraceparent,
  MetricsRegistry,
  parseTraceparent,
  traceMiddleware,
  type SpanRecord,
} from "../src/index.ts";

describe("production observability operations", () => {
  test("parses and formats strict W3C trace context", () => {
    const value = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
    const parsed = parseTraceparent(value);
    expect(parsed).toEqual({
      version: "00",
      traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
      spanId: "00f067aa0ba902b7",
      sampled: true,
    });
    expect(formatTraceparent(parsed!)).toBe(value);
    expect(parseTraceparent("00-00000000000000000000000000000000-00f067aa0ba902b7-01")).toBeNull();
    expect(parseTraceparent("ff-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")).toBeNull();
  });

  test("propagates trace context, correlates locals, and records a framework span", async () => {
    const parent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
    const ctx = createContext(
      new Request("https://example.test/orders", { headers: { traceparent: parent } }),
      new URL("https://example.test/orders"),
    );
    const spans: SpanRecord[] = [];
    const times = [100, 125];
    let randomValue = 1;
    const middleware = traceMiddleware({
      serviceName: "orders",
      now: () => times.shift() ?? 125,
      random: (target) => target.fill(randomValue++),
      onSpan: (span) => {
        spans.push(span);
      },
    });
    const response = await middleware(ctx, () => new Response("ok", { status: 201 }));

    expect(ctx.locals.traceId).toBe("4bf92f3577b34da6a3ce929d0e0e4736");
    expect(ctx.locals.requestId).toBe(ctx.locals.traceId);
    expect(response.headers.get("traceparent")).toMatch(
      /^00-4bf92f3577b34da6a3ce929d0e0e4736-[0-9a-f]{16}-01$/,
    );
    expect(response.headers.get("x-request-id")).toBe(String(ctx.locals.traceId));
    expect(spans[0]).toEqual(
      expect.objectContaining({
        parentSpanId: "00f067aa0ba902b7",
        durationMs: 25,
        status: "ok",
      }),
    );
    expect(ctx.tracer?.records()[0]).toEqual(expect.objectContaining({ status: "ok" }));
  });

  test("does not break responses when a telemetry exporter fails", async () => {
    const failures: unknown[] = [];
    const ctx = createContext(
      new Request("https://example.test/"),
      new URL("https://example.test/"),
    );
    const middleware = traceMiddleware({
      random: (target) => target.fill(7),
      exporter: { export: () => Promise.reject(new Error("collector unavailable")) },
      onExportError: (error) => {
        failures.push(error);
      },
    });
    const response = await middleware(ctx, () => new Response("ok"));
    expect(response.status).toBe(200);
    expect(failures).toHaveLength(1);
  });

  test("reports exporter failures through the non-recursive diagnostic hook", async () => {
    const diagnostics: Array<{ message: string; error: unknown }> = [];
    const ctx = createContext(
      new Request("https://example.test/"),
      new URL("https://example.test/"),
    );
    const middleware = traceMiddleware({
      random: (target) => target.fill(8),
      exporter: { export: () => Promise.reject(new Error("offline")) },
      diagnostic: (message, error) => diagnostics.push({ message, error }),
    });
    expect((await middleware(ctx, () => new Response("ok"))).status).toBe(200);
    expect(diagnostics).toHaveLength(1);
    expect(diagnostics[0]?.message).toBe("trace export failed");
  });

  test("exports OTLP JSON traces and metrics", async () => {
    const requests: unknown[] = [];
    const send = (async (_url: URL | RequestInfo, init?: RequestInit) => {
      requests.push(JSON.parse(String(init?.body)));
      return new Response(null, { status: 200 });
    }) as typeof fetch;
    const span: SpanRecord = {
      name: "GET /",
      traceId: "1".repeat(32),
      spanId: "2".repeat(16),
      sampled: true,
      startTime: 100,
      endTime: 125,
      durationMs: 25,
      status: "ok",
      attributes: { "http.response.status_code": 200 },
    };
    await createOtlpTraceExporter("https://collector.test/v1/traces", { fetch: send }).export([
      span,
    ]);
    const metrics = new MetricsRegistry(() => 200);
    metrics.increment("requests", 1, { route: "/" });
    await createOtlpMetricExporter("https://collector.test/v1/metrics", { fetch: send }).export(
      metrics.snapshot(),
    );
    expect(requests[0]).toHaveProperty(
      "resourceSpans.0.scopeSpans.0.spans.0.traceId",
      span.traceId,
    );
    expect(requests[1]).toHaveProperty(
      "resourceMetrics.0.scopeMetrics.0.metrics.0.name",
      "requests",
    );
  });

  test("serves liveness and dependency readiness without leaking details by default", async () => {
    const registry = new HealthRegistry();
    registry.register("database", () => ({ status: "down", message: "connection refused" }));
    const live = await createLivenessHandler()(new Request("https://example.test/live"));
    const ready = await createReadinessHandler(registry)(new Request("https://example.test/ready"));
    const detailed = await createReadinessHandler(registry, { exposeDetails: true })(
      new Request("https://example.test/ready"),
    );
    expect(live.status).toBe(200);
    expect(ready.status).toBe(503);
    expect(await ready.json()).toEqual({ status: "down" });
    expect(await detailed.json()).toHaveProperty("checks.database.message", "connection refused");
  });

  test("creates correlated structured child logs with secret redaction", () => {
    const records: unknown[] = [];
    const logger = createStructuredLogger({
      service: "api",
      now: () => new Date("2026-08-02T00:00:00.000Z"),
      sink: (record) => records.push(record),
    }).child({ traceId: "trace", requestId: "request" });
    logger.info("signed in", { userId: "user-1", token: "secret-value" });
    expect(records[0]).toEqual(
      expect.objectContaining({
        service: "api",
        traceId: "trace",
        requestId: "request",
        attributes: expect.objectContaining({ token: "[REDACTED]", userId: "user-1" }),
      }),
    );
  });
});
