import type { GetRaceProtestsResult } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
import { RaceProtestsPresenter } from './RaceProtestsPresenter';

describe('RaceProtestsPresenter', () => {
  let presenter: RaceProtestsPresenter;

  beforeEach(() => {
    presenter = new RaceProtestsPresenter();
  });

  describe('present', () => {
    it('should map result to response model', () => {
      const mockProtest = {
        id: 'protest-123',
        protestingDriverId: 'driver-456',
        accusedDriverId: 'driver-789',
        incident: {
          lap: { toNumber: () => 5 } as never,
          description: { toString: () => 'Contact at turn 3' } as never,
        },
        status: { toString: () => 'pending' } as never,
        filedAt: new Date('2024-01-01T10:30:00Z'),
      };

      const mockDriver1 = {
        id: 'driver-456',
        name: { toString: () => 'John Doe' } as never,
      };

      const mockDriver2 = {
        id: 'driver-789',
        name: { toString: () => 'Jane Smith' } as never,
      };

      const result: GetRaceProtestsResult = {
        protests: [mockProtest as never],
        drivers: [mockDriver1 as never, mockDriver2 as never],
      };

      presenter.present(result);

      expect(presenter.responseModel).toEqual({
        protests: [
          {
            id: 'protest-123',
            protestingDriverId: 'driver-456',
            accusedDriverId: 'driver-789',
            incident: {
              lap: 5,
              description: 'Contact at turn 3',
            },
            status: 'pending',
            filedAt: '2024-01-01T10:30:00.000Z',
          },
        ],
        driverMap: {
          'driver-456': 'John Doe',
          'driver-789': 'Jane Smith',
        },
      });
    });

    it('should handle multiple protests and drivers', () => {
      const result: GetRaceProtestsResult = {
        protests: [
          {
            id: 'protest-1',
            protestingDriverId: 'driver-1',
            accusedDriverId: 'driver-2',
            incident: { 
              lap: { toNumber: () => 3 } as never, 
              description: { toString: () => 'Incident 1' } as never 
            },
            status: { toString: () => 'pending' } as never,
            filedAt: new Date('2024-01-01T10:00:00Z'),
          } as never,
          {
            id: 'protest-2',
            protestingDriverId: 'driver-3',
            accusedDriverId: 'driver-1',
            incident: { 
              lap: { toNumber: () => 7 } as never, 
              description: { toString: () => 'Incident 2' } as never 
            },
            status: { toString: () => 'reviewed' } as never,
            filedAt: new Date('2024-01-01T10:10:00Z'),
          } as never,
        ],
        drivers: [
          { id: 'driver-1', name: { toString: () => 'Driver One' } } as never,
          { id: 'driver-2', name: { toString: () => 'Driver Two' } } as never,
          { id: 'driver-3', name: { toString: () => 'Driver Three' } } as never,
        ],
      };

      presenter.present(result);

      expect(presenter.responseModel.protests).toHaveLength(2);
      expect(presenter.responseModel.driverMap).toEqual({
        'driver-1': 'Driver One',
        'driver-2': 'Driver Two',
        'driver-3': 'Driver Three',
      });
    });

    it('should handle empty protests', () => {
      const result: GetRaceProtestsResult = {
        protests: [],
        drivers: [],
      };

      presenter.present(result);

      expect(presenter.responseModel).toEqual({
        protests: [],
        driverMap: {},
      });
    });

    it('should handle protests with reviewed status', () => {
      const result: GetRaceProtestsResult = {
        protests: [
          {
            id: 'protest-1',
            protestingDriverId: 'driver-1',
            accusedDriverId: 'driver-2',
            incident: { 
              lap: { toNumber: () => 5 } as never, 
              description: { toString: () => 'Test' } as never 
            },
            status: { toString: () => 'approved' } as never,
            filedAt: new Date('2024-01-01T10:00:00Z'),
          } as never,
        ],
        drivers: [
          { id: 'driver-1', name: { toString: () => 'Driver One' } } as never,
          { id: 'driver-2', name: { toString: () => 'Driver Two' } } as never,
        ],
      };

      presenter.present(result);

      expect(presenter.responseModel.protests[0]?.status).toBe('approved');
    });
  });

  describe('reset', () => {
    it('should clear the model', () => {
      presenter.present({
        protests: [{ 
          id: 'p1', 
          protestingDriverId: 'd1', 
          accusedDriverId: 'd2', 
          incident: { 
            lap: { toNumber: () => 1 } as never, 
            description: { toString: () => 'test' } as never 
          }, 
          status: { toString: () => 'pending' } as never, 
          filedAt: new Date() 
        } as never],
        drivers: [{ id: 'd1', name: { toString: () => 'D1' } } as never, { id: 'd2', name: { toString: () => 'D2' } } as never],
      });
      expect(presenter.responseModel).toBeDefined();

      presenter.reset();
      expect(() => presenter.responseModel).toThrow('Presenter not presented');
    });
  });

  describe('responseModel', () => {
    it('should throw error when accessed before present()', () => {
      expect(() => presenter.responseModel).toThrow('Presenter not presented');
    });
  });

  describe('viewModel', () => {
    it('should return same as responseModel', () => {
      const result: GetRaceProtestsResult = {
        protests: [{ 
          id: 'p1', 
          protestingDriverId: 'd1', 
          accusedDriverId: 'd2', 
          incident: { 
            lap: { toNumber: () => 1 } as never, 
            description: { toString: () => 'test' } as never 
          }, 
          status: { toString: () => 'pending' } as never, 
          filedAt: new Date() 
        } as never],
        drivers: [{ id: 'd1', name: { toString: () => 'D1' } } as never, { id: 'd2', name: { toString: () => 'D2' } } as never],
      };

      presenter.present(result);

      expect(presenter.viewModel).toEqual(presenter.responseModel);
    });
  });
});