import { TestBed } from '@angular/core/testing';
import { ErrorHandlerService } from './error-handler.service';
import { NotificationService } from './notification.service';

describe('ErrorHandlerService', () => {
  let service: ErrorHandlerService;
  let notificationSpy: jasmine.SpyObj<NotificationService>;

  beforeEach(() => {
    notificationSpy = jasmine.createSpyObj('NotificationService', ['error', 'success', 'info']);

    TestBed.configureTestingModule({
      providers: [
        ErrorHandlerService,
        { provide: NotificationService, useValue: notificationSpy },
      ],
    });

    service = TestBed.inject(ErrorHandlerService);
  });

  describe('parseError', () => {
    describe('Apollo GraphQL errors', () => {
      it('should parse MAX_ENROLLMENTS error code', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Maximum enrollments reached',
              extensions: { code: 'MAX_ENROLLMENTS' },
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('MAX_ENROLLMENTS');
        expect(result.userMessage).toBe('Has alcanzado el límite máximo de 3 materias inscritas');
        expect(result.suggestion).toBe('Debes cancelar una inscripción antes de agregar otra materia');
      });

      it('should parse SAME_PROFESSOR error code', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Already have subject with professor',
              extensions: { code: 'SAME_PROFESSOR' },
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('SAME_PROFESSOR');
        expect(result.userMessage).toBe('Ya tienes una materia con este profesor');
      });

      it('should parse STUDENT_NOT_FOUND error code', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Student not found',
              extensions: { code: 'STUDENT_NOT_FOUND' },
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('STUDENT_NOT_FOUND');
        expect(result.userMessage).toBe('El estudiante no existe en el sistema');
      });

      it('should parse DUPLICATE_ENROLLMENT error code', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Already enrolled',
              extensions: { code: 'DUPLICATE_ENROLLMENT' },
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('DUPLICATE_ENROLLMENT');
        expect(result.userMessage).toBe('Ya estás inscrito en esta materia');
      });

      it('should parse VALIDATION_ERROR with field details', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Validation failed',
              extensions: {
                code: 'VALIDATION_ERROR',
                errors: [
                  { PropertyName: 'Email', ErrorMessage: 'Invalid email format' },
                  { PropertyName: 'Name', ErrorMessage: 'Name is required' },
                ],
              },
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('VALIDATION_ERROR');
        expect(result.userMessage).toBe('Los datos ingresados no son válidos');
        expect(result.devMessage).toContain('Email: Invalid email format');
        expect(result.devMessage).toContain('Name: Name is required');
      });

      it('should handle unknown error code with UNKNOWN message', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Some unknown error',
              extensions: { code: 'WEIRD_ERROR' },
            },
          ],
        };

        const result = service.parseError(error);

        // Service preserves original code for logging but uses UNKNOWN's message
        expect(result.userMessage).toBe('Ocurrió un error inesperado');
      });

      it('should handle missing error code as UNKNOWN', () => {
        const error = {
          graphQLErrors: [
            {
              message: 'Error without code',
              extensions: {},
            },
          ],
        };

        const result = service.parseError(error);

        expect(result.code).toBe('UNKNOWN');
      });
    });

    describe('Network errors', () => {
      it('should parse network error with status 0', () => {
        const error = {
          networkError: { status: 0, message: 'Network failed' },
          message: 'Network error',
        };

        const result = service.parseError(error);

        expect(result.code).toBe('NETWORK_ERROR');
        expect(result.userMessage).toBe('No se pudo conectar con el servidor');
        expect(result.suggestion).toContain('Verifica tu conexión');
      });

      it('should parse network error with HTTP status', () => {
        const error = {
          networkError: { status: 503, message: 'Service Unavailable' },
          message: 'Network error',
        };

        const result = service.parseError(error);

        expect(result.code).toBe('NETWORK_ERROR');
        expect(result.devMessage).toContain('503');
      });
    });

    describe('HTTP errors', () => {
      it('should parse HTTP 500 as SERVER_ERROR', () => {
        const error = { status: 500, message: 'Internal Server Error' };

        const result = service.parseError(error);

        expect(result.code).toBe('SERVER_ERROR');
        expect(result.userMessage).toBe('Error interno del servidor');
      });

      it('should parse HTTP 502 as SERVER_ERROR', () => {
        const error = { status: 502, message: 'Bad Gateway' };

        const result = service.parseError(error);

        expect(result.code).toBe('SERVER_ERROR');
      });

      it('should parse HTTP 0 as NETWORK_ERROR', () => {
        const error = { status: 0, message: 'No response' };

        const result = service.parseError(error);

        expect(result.code).toBe('NETWORK_ERROR');
      });

      it('should parse HTTP 400 as UNKNOWN', () => {
        const error = { status: 400, message: 'Bad Request' };

        const result = service.parseError(error);

        expect(result.code).toBe('UNKNOWN');
      });
    });

    describe('Unknown errors', () => {
      it('should handle string error', () => {
        const error = 'Something went wrong';

        const result = service.parseError(error);

        expect(result.code).toBe('UNKNOWN');
        expect(result.devMessage).toBe('Something went wrong');
      });

      it('should handle null error', () => {
        const result = service.parseError(null);

        expect(result.code).toBe('UNKNOWN');
      });

      it('should handle undefined error', () => {
        const result = service.parseError(undefined);

        expect(result.code).toBe('UNKNOWN');
      });
    });
  });

  describe('handle', () => {
    it('should show notification with user message', () => {
      const error = {
        graphQLErrors: [
          {
            message: 'Maximum reached',
            extensions: { code: 'MAX_ENROLLMENTS' },
          },
        ],
      };

      service.handle(error);

      expect(notificationSpy.error).toHaveBeenCalledWith(
        'Has alcanzado el límite máximo de 3 materias inscritas. Debes cancelar una inscripción antes de agregar otra materia'
      );
    });

    it('should show notification without suggestion when not available', () => {
      const error = {
        graphQLErrors: [
          {
            message: 'Duplicate',
            extensions: { code: 'DUPLICATE_ENROLLMENT' },
          },
        ],
      };

      service.handle(error);

      expect(notificationSpy.error).toHaveBeenCalledWith('Ya estás inscrito en esta materia');
    });

    it('should return ErrorInfo', () => {
      const error = {
        graphQLErrors: [
          {
            message: 'Not found',
            extensions: { code: 'STUDENT_NOT_FOUND' },
          },
        ],
      };

      const result = service.handle(error);

      expect(result.code).toBe('STUDENT_NOT_FOUND');
      expect(result.userMessage).toBeTruthy();
    });

    it('should include context in log output', () => {
      spyOn(console, 'group'); // Prevent console output during test
      const error = { status: 500, message: 'Error' };

      service.handle(error, 'StudentService.create');

      // In dev mode, console.group is called
      // This test verifies the method doesn't throw
      expect(true).toBeTruthy();
    });
  });

  describe('logError', () => {
    it('should not show notification', () => {
      const error = {
        graphQLErrors: [
          {
            message: 'Error',
            extensions: { code: 'STUDENT_NOT_FOUND' },
          },
        ],
      };

      service.logError(error);

      expect(notificationSpy.error).not.toHaveBeenCalled();
    });
  });
});
