// packages/core-shared/src/instrumentation/sentry/sentry-logger.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";

vi.mock("@sentry/nextjs", () => ({
  captureException: vi.fn(),
  captureMessage: vi.fn(),
  addBreadcrumb: vi.fn(),
  setUser: vi.fn(),
}));

import * as Sentry from "@sentry/nextjs";
import { SentryLogger } from "@/instrumentation/sentry/sentry-logger";

describe("SentryLogger", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("captureException forwards to Sentry on first call", () => {
    const logger = new SentryLogger();
    const err = new Error("boom");
    logger.captureException(err, { tags: { feature: "blog" } });
    expect(Sentry.captureException).toHaveBeenCalledTimes(1);
    expect((Sentry.captureException as ReturnType<typeof vi.fn>).mock.calls[0]![0]).toBe(err);
  });

  it("captureException is a no-op when err already marked __sentryReported", () => {
    const logger = new SentryLogger();
    const err = new Error("already-reported");
    Object.defineProperty(err, "__sentryReported", { value: true });
    logger.captureException(err);
    expect(Sentry.captureException).not.toHaveBeenCalled();
  });

  it("captureException marks err as __sentryReported after sending", () => {
    const logger = new SentryLogger();
    const err = new Error("once");
    logger.captureException(err);
    expect((err as unknown as { __sentryReported: boolean }).__sentryReported).toBe(true);
    // Second call: no-op
    logger.captureException(err);
    expect(Sentry.captureException).toHaveBeenCalledTimes(1);
  });

  it("__sentryReported is non-enumerable", () => {
    const logger = new SentryLogger();
    const err = new Error("x");
    logger.captureException(err);
    expect(Object.keys(err)).not.toContain("__sentryReported");
    expect(JSON.stringify(err)).not.toContain("__sentryReported");
  });

  it("captureMessage forwards to Sentry", () => {
    const logger = new SentryLogger();
    logger.captureMessage("hello", "warning", { tags: { foo: "bar" } });
    expect(Sentry.captureMessage).toHaveBeenCalledWith(
      "hello",
      expect.objectContaining({ level: "warning" }),
    );
  });

  it("addBreadcrumb forwards to Sentry", () => {
    const logger = new SentryLogger();
    logger.addBreadcrumb({ category: "test", message: "x", data: { k: "v" } });
    expect(Sentry.addBreadcrumb).toHaveBeenCalledTimes(1);
  });

  it("setUser strips non-id keys and warns in dev", () => {
    const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
    const logger = new SentryLogger();
    logger.setUser({ id: "u1", email: "a@b.c", username: "alice" } as unknown as { id: string });
    expect(Sentry.setUser).toHaveBeenCalledWith({ id: "u1" });
    expect(warn).toHaveBeenCalled();
    warn.mockRestore();
  });

  it("setUser passes null through", () => {
    const logger = new SentryLogger();
    logger.setUser(null);
    expect(Sentry.setUser).toHaveBeenCalledWith(null);
  });
});
