import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { context, trace } from "@opentelemetry/api";
import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { currentTraceId } from "./current-trace-id";

// Register async context manager so startActiveSpan propagates context.
const ctxManager = new AsyncLocalStorageContextManager();
ctxManager.enable();
context.setGlobalContextManager(ctxManager);

function setupProvider(): { exporter: InMemorySpanExporter; provider: BasicTracerProvider } {
  const exporter = new InMemorySpanExporter();
  const provider = new BasicTracerProvider({
    spanProcessors: [new SimpleSpanProcessor(exporter)],
  });
  trace.setGlobalTracerProvider(provider);
  return { exporter, provider };
}

describe("currentTraceId", () => {
  let exporter: InMemorySpanExporter;
  let provider: BasicTracerProvider;

  beforeEach(() => {
    ({ exporter, provider } = setupProvider());
  });

  afterEach(async () => {
    await provider.shutdown();
    trace.disable();
  });

  it("returns undefined when no active span", () => {
    expect(currentTraceId()).toBeUndefined();
  });

  it("returns the active span's traceId when inside startActiveSpan", async () => {
    const tracer = trace.getTracer("test");
    await new Promise<void>((resolve) => {
      tracer.startActiveSpan("test-span", (span) => {
        const id = currentTraceId();
        expect(id).toBeDefined();
        expect(id).toMatch(/^[a-f0-9]{32}$/);
        span.end();
        resolve();
      });
    });
  });

  it("filters all-zeros invalid traceId", () => {
    // The INVALID_SPAN (no-op) has traceId "00000000000000000000000000000000"
    // which is what getActiveSpan() returns when there is no real span.
    // currentTraceId() must treat this as absent.
    expect(currentTraceId()).toBeUndefined();
  });

  // Suppress unused variable warning — exporter used via closure
  it("returns distinct traceIds for independent spans", async () => {
    const tracer = trace.getTracer("test");
    const ids: string[] = [];
    for (let i = 0; i < 2; i++) {
      await new Promise<void>((resolve) => {
        tracer.startActiveSpan(`span-${i}`, (span) => {
          const id = currentTraceId();
          if (id) ids.push(id);
          span.end();
          resolve();
        });
      });
    }
    // Both spans are in fresh traces — IDs should be valid hex strings
    expect(ids).toHaveLength(2);
    for (const id of ids) {
      expect(id).toMatch(/^[a-f0-9]{32}$/);
    }
    void exporter; // suppress lint
  });
});
