// src/tests/e2e/error-reporting.e2e.test.ts
/**
 * E2E tests for error reporting to Bugsink/Sentry (ADR-015).
 *
 * These tests verify that errors are properly captured and can be sent
 * to the error tracking system. They test both the backend (Express/Node)
 * and frontend (React) error handling paths.
 *
 * Note: These tests don't actually verify Bugsink receives the errors
 * (that would require Bugsink to be running). Instead, they verify:
 * 1. The Sentry SDK is properly initialized
 * 2. Errors trigger the capture functions
 * 3. The middleware chain works correctly
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import express from 'express';
import request from 'supertest';

/**
 * @vitest-environment node
 */

describe('Error Reporting E2E', () => {
  describe('Backend Error Handling', () => {
    let app: express.Application;
    let mockCaptureException: ReturnType<typeof vi.fn>;

    beforeEach(() => {
      // Reset modules to get fresh instances
      vi.resetModules();

      // Mock Sentry before importing the module
      mockCaptureException = vi.fn().mockReturnValue('mock-event-id');
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: mockCaptureException,
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      // Create a test Express app with error handling
      app = express();
      app.use(express.json());

      // Test route that throws a 500 error
      app.get('/api/test/error-500', (_req, res, next) => {
        const error = new Error('Test 500 error for Sentry');
        (error as Error & { statusCode: number }).statusCode = 500;
        next(error);
      });

      // Test route that throws a 400 error (should NOT be sent to Sentry)
      app.get('/api/test/error-400', (_req, res, next) => {
        const error = new Error('Test 400 error');
        (error as Error & { statusCode: number }).statusCode = 400;
        next(error);
      });

      // Test route that succeeds
      app.get('/api/test/success', (_req, res) => {
        res.json({ success: true });
      });
    });

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

    it('should have a test endpoint that throws a 500 error', async () => {
      // Add error handler
      app.use(
        (
          err: Error & { statusCode?: number },
          _req: express.Request,
          res: express.Response,
          _next: express.NextFunction,
        ) => {
          const statusCode = err.statusCode || 500;
          res.status(statusCode).json({ error: err.message });
        },
      );

      const response = await request(app).get('/api/test/error-500');

      expect(response.status).toBe(500);
      expect(response.body).toEqual({ error: 'Test 500 error for Sentry' });
    });

    it('should have a test endpoint that throws a 400 error', async () => {
      // Add error handler
      app.use(
        (
          err: Error & { statusCode?: number },
          _req: express.Request,
          res: express.Response,
          _next: express.NextFunction,
        ) => {
          const statusCode = err.statusCode || 500;
          res.status(statusCode).json({ error: err.message });
        },
      );

      const response = await request(app).get('/api/test/error-400');

      expect(response.status).toBe(400);
      expect(response.body).toEqual({ error: 'Test 400 error' });
    });

    it('should have a success endpoint that returns 200', async () => {
      const response = await request(app).get('/api/test/success');

      expect(response.status).toBe(200);
      expect(response.body).toEqual({ success: true });
    });
  });

  describe('Sentry Module Configuration', () => {
    it('should export initSentry function', async () => {
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      const { initSentry } = await import('../../services/sentry.server');
      expect(typeof initSentry).toBe('function');
    });

    it('should export getSentryMiddleware function', async () => {
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      const { getSentryMiddleware } = await import('../../services/sentry.server');
      expect(typeof getSentryMiddleware).toBe('function');
    });

    it('should export captureException function', async () => {
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      const { captureException } = await import('../../services/sentry.server');
      expect(typeof captureException).toBe('function');
    });

    it('should export captureMessage function', async () => {
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      const { captureMessage } = await import('../../services/sentry.server');
      expect(typeof captureMessage).toBe('function');
    });

    it('should export setUser function', async () => {
      vi.doMock('@sentry/node', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
      }));

      const { setUser } = await import('../../services/sentry.server');
      expect(typeof setUser).toBe('function');
    });
  });

  describe('Frontend Sentry Client Configuration', () => {
    beforeEach(() => {
      vi.resetModules();
    });

    it('should export initSentry function for frontend', async () => {
      // Mock @sentry/react
      vi.doMock('@sentry/react', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
        breadcrumbsIntegration: vi.fn(() => ({})),
        ErrorBoundary: vi.fn(() => null),
      }));

      // Mock the config
      vi.doMock('../../config', () => ({
        default: {
          sentry: {
            dsn: '',
            environment: 'test',
            debug: false,
            enabled: false,
          },
        },
      }));

      const { initSentry } = await import('../../services/sentry.client');
      expect(typeof initSentry).toBe('function');
    });

    it('should export captureException function for frontend', async () => {
      vi.doMock('@sentry/react', () => ({
        init: vi.fn(),
        captureException: vi.fn(),
        captureMessage: vi.fn(),
        setUser: vi.fn(),
        setContext: vi.fn(),
        addBreadcrumb: vi.fn(),
        breadcrumbsIntegration: vi.fn(() => ({})),
        ErrorBoundary: vi.fn(() => null),
      }));

      vi.doMock('../../config', () => ({
        default: {
          sentry: {
            dsn: '',
            environment: 'test',
            debug: false,
            enabled: false,
          },
        },
      }));

      const { captureException } = await import('../../services/sentry.client');
      expect(typeof captureException).toBe('function');
    });
  });
});
