import { describe, expect, it } from "vitest";

import type {
  SentryEventHint,
  SentryEventPolicyOptions,
  SentryPolicyEvent,
} from "./event-policy";
import {
  applySentryEventPolicy,
  isErrorAlreadyReported,
  isErrorCapturedBySentry,
  markErrorAsReported,
} from "./event-policy";

describe("applySentryEventPolicy", () => {
  describe("when events are actionable", () => {
    it("should keep error events", () => {
      // Given
      const event = {
        level: "error",
        message: "Unexpected failure",
      } satisfies SentryPolicyEvent & { level: string; message: string };
      const options: SentryEventPolicyOptions = { source: "client" };

      // When
      const result = applySentryEventPolicy(event, undefined, options);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
          source: "client",
        },
      });
    });

    it("should keep fatal events", () => {
      // Given
      const event = { level: "fatal", message: "Runtime crashed" };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
    });

    it("should keep events without a level", () => {
      // Given
      const event = { message: "Default Sentry event" };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
    });

    it("should keep fatal runtime messages that contain an expected HTTP status number", () => {
      // Given
      const event = {
        level: "fatal",
        message: "Runtime worker 401 crashed unexpectedly",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
        },
      });
    });

    it("should keep runtime error messages that contain an expected HTTP status number", () => {
      // Given
      const event = {
        level: "error",
        message: "Background import 404 failed after a null dereference",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
        },
      });
    });

    it("should keep fatal runtime messages that mention status without HTTP context", () => {
      // Given
      const event = {
        level: "fatal",
        message: "State transition status 403 caused worker crash",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
        },
      });
    });

    it("should keep fatal runtime messages that mention status code without HTTP context", () => {
      // Given
      const event = {
        level: "fatal",
        message: "State transition status code 403 caused worker crash",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
        },
      });
    });

    it("should keep fatal runtime messages that mention response without HTTP context", () => {
      // Given
      const event = {
        level: "fatal",
        message: "Worker response 404 triggered fatal cache corruption",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          kind: "runtime",
        },
      });
    });

    it("should preserve existing tags on actionable API events", () => {
      // Given
      const event = {
        level: "error",
        message: "API response failed with status 500",
        tags: {
          feature: "providers",
          status_code: "500",
        },
      };

      // When
      const result = applySentryEventPolicy(event, undefined, {
        source: "server_action",
      });

      // Then
      expect(result).toBe(event);
      expect(result).toMatchObject({
        tags: {
          actionability: "actionable",
          feature: "providers",
          kind: "api",
          source: "server_action",
          status_code: "500",
        },
      });
    });
  });

  describe("when events are non-actionable", () => {
    it("should drop warning events", () => {
      // Given
      const event = { level: "warning", message: "Provider already exists" };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBeNull();
    });

    it("should drop Next.js redirect control-flow events", () => {
      // Given
      const event = { level: "error", message: "NEXT_REDIRECT" };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBeNull();
    });

    it("should drop AbortError events", () => {
      // Given
      const event = { level: "error", message: "The operation was aborted" };
      const hint: SentryEventHint = {
        originalException: new DOMException("Aborted", "AbortError"),
      };

      // When
      const result = applySentryEventPolicy(event, hint);

      // Then
      expect(result).toBeNull();
    });

    it("should drop expected HTTP 401 events", () => {
      // Given
      const event = { level: "error", tags: { status_code: "401" } };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBeNull();
    });

    it("should drop expected HTTP 403 events", () => {
      // Given
      const event = { level: "error", message: "Request failed with 403" };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBeNull();
    });

    it("should drop expected HTTP 404 events", () => {
      // Given
      const event = { level: "error" };
      const hint = { originalException: { status: 404 } };

      // When
      const result = applySentryEventPolicy(event, hint);

      // Then
      expect(result).toBeNull();
    });

    it("should drop clear HTTP messages with expected status codes", () => {
      // Given
      const event = {
        level: "error",
        message: "HTTP response status code 404",
      };

      // When
      const result = applySentryEventPolicy(event);

      // Then
      expect(result).toBeNull();
    });

    it("should drop already-reported errors", () => {
      // Given
      const error = new Error("Already reported failure");
      const event = { level: "error", message: error.message };
      markErrorAsReported(error);

      // When
      const result = applySentryEventPolicy(event, {
        originalException: error,
      });

      // Then
      expect(isErrorAlreadyReported(error)).toBe(true);
      expect(result).toBeNull();
    });

    it("should keep the first event for an error marked by Sentry", () => {
      // Given
      const error = new Error("First capture");
      Object.defineProperty(error, "__sentry_captured__", { value: true });
      const event = { level: "error", message: error.message };

      // When
      const result = applySentryEventPolicy(event, {
        originalException: error,
      });

      // Then
      expect(isErrorCapturedBySentry(error)).toBe(true);
      expect(result).toBe(event);
    });
  });
});
