// src/services/sentry.client.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// Use vi.hoisted to define mocks that need to be available before vi.mock runs
const { mockSentry, mockLogger } = vi.hoisted(() => ({
  mockSentry: {
    init: vi.fn(),
    captureException: vi.fn(() => 'mock-event-id'),
    captureMessage: vi.fn(() => 'mock-message-id'),
    setContext: vi.fn(),
    setUser: vi.fn(),
    addBreadcrumb: vi.fn(),
    breadcrumbsIntegration: vi.fn(() => ({})),
    ErrorBoundary: vi.fn(),
  },
  mockLogger: {
    info: vi.fn(),
    debug: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  },
}));

vi.mock('@sentry/react', () => mockSentry);

vi.mock('./logger.client', () => ({
  logger: mockLogger,
  default: mockLogger,
}));

describe('sentry.client', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

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

  describe('with Sentry disabled (default test environment)', () => {
    // The test environment has Sentry disabled by default (VITE_SENTRY_DSN not set)
    // Import the module fresh for each test

    beforeEach(() => {
      vi.resetModules();
    });

    it('should have isSentryConfigured as false in test environment', async () => {
      const { isSentryConfigured } = await import('./sentry.client');
      expect(isSentryConfigured).toBe(false);
    });

    it('should not initialize Sentry when not configured', async () => {
      const { initSentry, isSentryConfigured } = await import('./sentry.client');

      initSentry();

      // When Sentry is not configured, Sentry.init should NOT be called
      if (!isSentryConfigured) {
        expect(mockSentry.init).not.toHaveBeenCalled();
      }
    });

    it('should return undefined from captureException when not configured', async () => {
      const { captureException } = await import('./sentry.client');

      const result = captureException(new Error('test error'));

      expect(result).toBeUndefined();
      expect(mockSentry.captureException).not.toHaveBeenCalled();
    });

    it('should return undefined from captureMessage when not configured', async () => {
      const { captureMessage } = await import('./sentry.client');

      const result = captureMessage('test message');

      expect(result).toBeUndefined();
      expect(mockSentry.captureMessage).not.toHaveBeenCalled();
    });

    it('should not set user when not configured', async () => {
      const { setUser } = await import('./sentry.client');

      setUser({ id: '123', email: 'test@example.com' });

      expect(mockSentry.setUser).not.toHaveBeenCalled();
    });

    it('should not add breadcrumb when not configured', async () => {
      const { addBreadcrumb } = await import('./sentry.client');

      addBreadcrumb({ message: 'test breadcrumb', category: 'test' });

      expect(mockSentry.addBreadcrumb).not.toHaveBeenCalled();
    });
  });

  describe('Sentry re-export', () => {
    it('should re-export Sentry object', async () => {
      const { Sentry } = await import('./sentry.client');

      expect(Sentry).toBeDefined();
      expect(Sentry.init).toBeDefined();
      expect(Sentry.captureException).toBeDefined();
    });
  });

  describe('initSentry beforeSend filter logic', () => {
    // Test the beforeSend filter function logic in isolation
    // This tests the filter that's passed to Sentry.init

    it('should filter out browser extension errors', () => {
      // Simulate the beforeSend logic from the implementation
      const filterExtensionErrors = (event: {
        exception?: {
          values?: Array<{
            stacktrace?: {
              frames?: Array<{ filename?: string }>;
            };
          }>;
        };
      }) => {
        if (
          event.exception?.values?.[0]?.stacktrace?.frames?.some((frame) =>
            frame.filename?.includes('extension://'),
          )
        ) {
          return null;
        }
        return event;
      };

      const extensionError = {
        exception: {
          values: [
            {
              stacktrace: {
                frames: [{ filename: 'chrome-extension://abc123/script.js' }],
              },
            },
          ],
        },
      };

      expect(filterExtensionErrors(extensionError)).toBeNull();
    });

    it('should allow normal errors through', () => {
      const filterExtensionErrors = (event: {
        exception?: {
          values?: Array<{
            stacktrace?: {
              frames?: Array<{ filename?: string }>;
            };
          }>;
        };
      }) => {
        if (
          event.exception?.values?.[0]?.stacktrace?.frames?.some((frame) =>
            frame.filename?.includes('extension://'),
          )
        ) {
          return null;
        }
        return event;
      };

      const normalError = {
        exception: {
          values: [
            {
              stacktrace: {
                frames: [{ filename: '/app/src/index.js' }],
              },
            },
          ],
        },
      };

      expect(filterExtensionErrors(normalError)).toBe(normalError);
    });

    it('should handle events without exception property', () => {
      const filterExtensionErrors = (event: {
        exception?: {
          values?: Array<{
            stacktrace?: {
              frames?: Array<{ filename?: string }>;
            };
          }>;
        };
      }) => {
        if (
          event.exception?.values?.[0]?.stacktrace?.frames?.some((frame) =>
            frame.filename?.includes('extension://'),
          )
        ) {
          return null;
        }
        return event;
      };

      const eventWithoutException = { message: 'test' };

      expect(filterExtensionErrors(eventWithoutException as any)).toBe(eventWithoutException);
    });

    it('should handle firefox extension URLs', () => {
      const filterExtensionErrors = (event: {
        exception?: {
          values?: Array<{
            stacktrace?: {
              frames?: Array<{ filename?: string }>;
            };
          }>;
        };
      }) => {
        if (
          event.exception?.values?.[0]?.stacktrace?.frames?.some((frame) =>
            frame.filename?.includes('extension://'),
          )
        ) {
          return null;
        }
        return event;
      };

      const firefoxExtensionError = {
        exception: {
          values: [
            {
              stacktrace: {
                frames: [{ filename: 'moz-extension://abc123/script.js' }],
              },
            },
          ],
        },
      };

      expect(filterExtensionErrors(firefoxExtensionError)).toBeNull();
    });
  });

  describe('isSentryConfigured logic', () => {
    // Test the logic that determines if Sentry is configured
    // This mirrors the implementation: !!config.sentry.dsn && config.sentry.enabled

    it('should return false when DSN is empty', () => {
      const dsn = '';
      const enabled = true;
      const result = !!dsn && enabled;
      expect(result).toBe(false);
    });

    it('should return false when enabled is false', () => {
      const dsn = 'https://test@sentry.io/123';
      const enabled = false;
      const result = !!dsn && enabled;
      expect(result).toBe(false);
    });

    it('should return true when DSN is set and enabled is true', () => {
      const dsn = 'https://test@sentry.io/123';
      const enabled = true;
      const result = !!dsn && enabled;
      expect(result).toBe(true);
    });

    it('should return false when DSN is undefined', () => {
      const dsn = undefined;
      const enabled = true;
      const result = !!dsn && enabled;
      expect(result).toBe(false);
    });
  });

  describe('captureException logic', () => {
    it('should set context before capturing when context is provided', () => {
      // This tests the conditional context setting logic
      const context = { userId: '123' };
      const shouldSetContext = !!context;
      expect(shouldSetContext).toBe(true);
    });

    it('should not set context when not provided', () => {
      const context = undefined;
      const shouldSetContext = !!context;
      expect(shouldSetContext).toBe(false);
    });
  });

  describe('captureMessage default level', () => {
    it('should default to info level', () => {
      // Test the default parameter behavior
      const defaultLevel = 'info';
      expect(defaultLevel).toBe('info');
    });
  });
});
