import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AppError, errorCodes, handleError, createAPIError } from './errorHandling';
import * as analytics from './analytics';

// Mock the analytics module
vi.mock('./analytics', () => ({
  trackEvent: vi.fn()
}));

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

  describe('AppError', () => {
    it('should create an AppError with all properties', () => {
      const error = new AppError('Test error', 'TEST_CODE', 'error', { key: 'value' });

      expect(error).toBeInstanceOf(Error);
      expect(error).toBeInstanceOf(AppError);
      expect(error.message).toBe('Test error');
      expect(error.code).toBe('TEST_CODE');
      expect(error.severity).toBe('error');
      expect(error.metadata).toEqual({ key: 'value' });
      expect(error.name).toBe('AppError');
    });

    it('should create an AppError with default severity', () => {
      const error = new AppError('Test error', 'TEST_CODE');

      expect(error.severity).toBe('error');
      expect(error.metadata).toBeUndefined();
    });

    it('should create an AppError with warning severity', () => {
      const error = new AppError('Test warning', 'TEST_CODE', 'warning');

      expect(error.severity).toBe('warning');
    });

    it('should create an AppError with info severity', () => {
      const error = new AppError('Test info', 'TEST_CODE', 'info');

      expect(error.severity).toBe('info');
    });
  });

  describe('errorCodes', () => {
    it('should have all error codes defined', () => {
      expect(errorCodes.NETWORK_ERROR).toBe('ERR_NETWORK');
      expect(errorCodes.API_ERROR).toBe('ERR_API');
      expect(errorCodes.AUTH_ERROR).toBe('ERR_AUTH');
      expect(errorCodes.VALIDATION_ERROR).toBe('ERR_VALIDATION');
      expect(errorCodes.UNKNOWN_ERROR).toBe('ERR_UNKNOWN');
    });
  });

  describe('handleError', () => {
    it('should handle AppError and return it unchanged', () => {
      const originalError = new AppError('Test error', 'TEST_CODE', 'error', { foo: 'bar' });
      const result = handleError(originalError, 'test-context');

      expect(result).toBe(originalError);
      expect(result.message).toBe('Test error');
      expect(result.code).toBe('TEST_CODE');
      expect(result.severity).toBe('error');
      expect(result.metadata).toEqual({ foo: 'bar' });
      expect(analytics.trackEvent).toHaveBeenCalledWith(
        'Error',
        'TEST_CODE',
        'test-context: Test error'
      );
    });

    it('should convert regular Error to AppError', () => {
      const originalError = new Error('Regular error');
      const result = handleError(originalError, 'test-context');

      expect(result).toBeInstanceOf(AppError);
      expect(result.message).toBe('Regular error');
      expect(result.code).toBe(errorCodes.UNKNOWN_ERROR);
      expect(result.severity).toBe('error');
      expect(result.metadata).toEqual({ originalError });
      expect(analytics.trackEvent).toHaveBeenCalledWith(
        'Error',
        errorCodes.UNKNOWN_ERROR,
        'test-context: Regular error'
      );
    });

    it('should handle unknown error types', () => {
      const result = handleError('string error', 'test-context');

      expect(result).toBeInstanceOf(AppError);
      expect(result.message).toBe('An unexpected error occurred');
      expect(result.code).toBe(errorCodes.UNKNOWN_ERROR);
      expect(result.severity).toBe('error');
      expect(result.metadata).toEqual({ originalError: 'string error' });
      expect(analytics.trackEvent).toHaveBeenCalledWith(
        'Error',
        errorCodes.UNKNOWN_ERROR,
        'test-context: An unexpected error occurred'
      );
    });

    it('should handle null error', () => {
      const result = handleError(null, 'test-context');

      expect(result).toBeInstanceOf(AppError);
      expect(result.message).toBe('An unexpected error occurred');
      expect(result.code).toBe(errorCodes.UNKNOWN_ERROR);
      expect(result.metadata).toEqual({ originalError: null });
    });

    it('should handle undefined error', () => {
      const result = handleError(undefined, 'test-context');

      expect(result).toBeInstanceOf(AppError);
      expect(result.message).toBe('An unexpected error occurred');
      expect(result.code).toBe(errorCodes.UNKNOWN_ERROR);
      expect(result.metadata).toEqual({ originalError: undefined });
    });

    it('should track error in analytics', () => {
      const error = new AppError('Analytics test', 'ANALYTICS_CODE');
      handleError(error, 'analytics-context');

      expect(analytics.trackEvent).toHaveBeenCalledTimes(1);
      expect(analytics.trackEvent).toHaveBeenCalledWith(
        'Error',
        'ANALYTICS_CODE',
        'analytics-context: Analytics test'
      );
    });
  });

  describe('createAPIError', () => {
    it('should create an API error with status code', () => {
      const error = createAPIError(404, 'Not found');

      expect(error).toBeInstanceOf(AppError);
      expect(error.message).toBe('Not found');
      expect(error.code).toBe(errorCodes.API_ERROR);
      expect(error.severity).toBe('error');
      expect(error.metadata).toEqual({ status: 404 });
    });

    it('should create an API error for 500 status', () => {
      const error = createAPIError(500, 'Internal server error');

      expect(error).toBeInstanceOf(AppError);
      expect(error.message).toBe('Internal server error');
      expect(error.code).toBe(errorCodes.API_ERROR);
      expect(error.metadata).toEqual({ status: 500 });
    });

    it('should create an API error for 401 status', () => {
      const error = createAPIError(401, 'Unauthorized');

      expect(error).toBeInstanceOf(AppError);
      expect(error.message).toBe('Unauthorized');
      expect(error.metadata).toEqual({ status: 401 });
    });
  });
});
