import { Context } from "@opentelemetry/api";
import {
  SamplingDecision,
  SamplingResult,
} from "@opentelemetry/sdk-trace-base";
import { describe, expect, test, vi } from "vitest";
import { UrlSampler } from "../lib/url-sampler";

describe("url sampler", () => {
  // mock sampler to be sure every trace after UrlSamper has RECORD status
  const mockSampler = {
    shouldSample: vi
      .fn()
      .mockImplementation(
        (_context, _traceId, _spanName, _spanKind, attributes, _links) => {
          return {
            decision: SamplingDecision.RECORD,
            attributes: attributes,
          } as SamplingResult;
        },
      ),
  };

  test("should add custom span attributes to trace", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "endsWith",
          url: "/health",
        },
      ],
      mockSampler,
      {
        "signal.namespace": "unittest",
        "signal.callback.result": () => "test",
      },
    );

    expect(sampler).not.toBeNull();

    const result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/track" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.RECORD);
    expect(result.attributes).not.toBeNull();
  });

  test("should not record trace about /health api", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "endsWith",
          url: "/health",
        },
      ],
      mockSampler,
    );

    const result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/health" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.NOT_RECORD);
  });

  test("should record every other trace which is not /health api", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "endsWith",
          url: "/health",
        },
      ],
      mockSampler,
    );

    let result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/test" },
      [],
    );

    expect(result.decision).toBe(SamplingDecision.RECORD);

    result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/another/url" },
      [],
    );

    expect(result.decision).toBe(SamplingDecision.RECORD);
  });

  test("operator 'includes', should not record every trace which include /block in url", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "includes",
          url: "/block",
        },
      ],
      mockSampler,
    );

    expect(sampler).not.toBeNull();

    const result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/namespace/block/example/12" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.NOT_RECORD);
  });

  test("operator 'endsWith', should not record only trace which ends with /block in url", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "endsWith",
          url: "/block",
        },
      ],
      mockSampler,
    );

    expect(sampler).not.toBeNull();

    // expect traced with block in the URL middle
    let result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/namespace/block/example/12" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.RECORD);

    // should stop trace with block at the end
    result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/namespace/example/block" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.NOT_RECORD);
  });

  test("operator 'equals', should not record trace which is equal to /block in url", async () => {
    const sampler: UrlSampler = new UrlSampler(
      [
        {
          type: "equals",
          url: "/block",
        },
      ],
      mockSampler,
    );

    expect(sampler).not.toBeNull();

    let result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/namespace/block/example/12" },
      [],
    );

    expect(sampler.toString()).toBe("UrlSampler");
    expect(result.decision).toBe(SamplingDecision.RECORD);

    result = sampler.shouldSample(
      {} as Context,
      "traceId",
      "span",
      0,
      { "http.target": "/block" },
      [],
    );

    expect(result.decision).toBe(SamplingDecision.NOT_RECORD);
  });
});
