import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import { EMPTY_RUNTIME_PUBLIC_CONFIG } from "@/lib/runtime-config.shared";

// Stable mock fns shared across module re-evaluations (resetModules clears the
// module registry but these hoisted fns survive, so assertions stay reliable).
const { initMock, setUserMock, captureMock, getConfigMock } = vi.hoisted(
  () => ({
    initMock: vi.fn(),
    setUserMock: vi.fn(),
    captureMock: vi.fn(),
    getConfigMock: vi.fn(),
  }),
);

vi.mock("@sentry/nextjs", () => ({
  init: initMock,
  setUser: setUserMock,
  captureRouterTransitionStart: captureMock,
  browserTracingIntegration: vi.fn(() => ({})),
}));

vi.mock("@/lib/get-runtime-config.client", () => ({
  getRuntimeConfigClient: getConfigMock,
}));

vi.mock(
  "@/components/shadcn/navigation-progress/use-navigation-progress",
  () => ({
    startProgress: vi.fn(),
    cancelProgress: vi.fn(),
  }),
);

describe("instrumentation-client Sentry init", () => {
  beforeEach(() => {
    // Re-evaluate the module per case so its top-level init guard re-runs.
    vi.resetModules();
    initMock.mockClear();
    setUserMock.mockClear();
    captureMock.mockClear();
    getConfigMock.mockReset();
  });

  afterEach(() => {
    vi.unstubAllEnvs();
  });

  it("initializes Sentry with the runtime DSN and the build-time release when a DSN is present", async () => {
    // Given
    getConfigMock.mockReturnValue({
      ...EMPTY_RUNTIME_PUBLIC_CONFIG,
      sentryDsn: "https://key@o0.ingest.sentry.io/1",
      sentryEnvironment: "pro",
    });
    // The browser release comes from the build-time public version (D10);
    // SENTRY_RELEASE is non-public and never reaches the client.
    vi.stubEnv("NEXT_PUBLIC_PROWLER_RELEASE_VERSION", "1.30.0");

    // When
    await import("@/instrumentation-client");

    // Then
    expect(initMock).toHaveBeenCalledTimes(1);
    expect(initMock.mock.calls[0][0]).toMatchObject({
      dsn: "https://key@o0.ingest.sentry.io/1",
      environment: "pro",
      release: "1.30.0",
    });
  });

  it("does not initialize Sentry when the DSN is absent", async () => {
    // Given
    getConfigMock.mockReturnValue({ ...EMPTY_RUNTIME_PUBLIC_CONFIG });

    // When
    await import("@/instrumentation-client");

    // Then
    expect(initMock).not.toHaveBeenCalled();
  });

  it("defaults to a non-dev environment so an unset UI_SENTRY_ENVIRONMENT does not enable dev mode", async () => {
    // Given - DSN set but environment unset
    getConfigMock.mockReturnValue({
      ...EMPTY_RUNTIME_PUBLIC_CONFIG,
      sentryDsn: "https://key@o0.ingest.sentry.io/1",
    });

    // When
    await import("@/instrumentation-client");

    // Then
    const options = initMock.mock.calls[0][0];
    expect(options.environment).toBe("production");
    expect(options.debug).toBe(false);
    expect(options.tracesSampleRate).toBe(0.5);
    expect(setUserMock).not.toHaveBeenCalled();
  });
});
