import { beforeEach, describe, expect, it } from 'vitest';
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
import { DriverId } from '../DriverId';
import { RaceId } from '../RaceId';
import { IncidentCount } from './IncidentCount';
import { LapTime } from './LapTime';
import { Position } from './Position';
import { Result } from './Result';

describe('Result', () => {
  const validId = 'result-123';
  const validRaceId = 'race-456';
  const validDriverId = 'driver-789';
  const validPosition = 1;
  const validFastestLap = 95.5;
  const validIncidents = 0;
  const validStartPosition = 2;

  describe('create', () => {
    it('should create a Result with valid props', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      const result = Result.create(props);

      expect(result.id).toBe(validId);
      expect(result.raceId.toString()).toBe(validRaceId);
      expect(result.driverId.toString()).toBe(validDriverId);
      expect(result.position.toNumber()).toBe(validPosition);
      expect(result.fastestLap.toNumber()).toBe(validFastestLap);
      expect(result.incidents.toNumber()).toBe(validIncidents);
      expect(result.startPosition.toNumber()).toBe(validStartPosition);
    });

    it('should throw error for empty id', () => {
      const props = {
        id: '',
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for empty raceId', () => {
      const props = {
        id: validId,
        raceId: '',
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for empty driverId', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: '',
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for invalid position', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: 0,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for invalid fastestLap', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: -1,
        incidents: validIncidents,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for invalid incidents', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: -1,
        startPosition: validStartPosition,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });

    it('should throw error for invalid startPosition', () => {
      const props = {
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: 0,
      };

      expect(() => Result.create(props)).toThrow(RacingDomainValidationError);
    });
  });

  describe('entity properties', () => {
    let result: Result;

    beforeEach(() => {
      result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      });
    });

    it('should have readonly id', () => {
      expect(result.id).toBe(validId);
    });

    it('should have readonly raceId as RaceId', () => {
      expect(result.raceId).toBeInstanceOf(RaceId);
      expect(result.raceId.toString()).toBe(validRaceId);
    });

    it('should have readonly driverId as DriverId', () => {
      expect(result.driverId).toBeInstanceOf(DriverId);
      expect(result.driverId.toString()).toBe(validDriverId);
    });

    it('should have readonly position as Position', () => {
      expect(result.position).toBeInstanceOf(Position);
      expect(result.position.toNumber()).toBe(validPosition);
    });

    it('should have readonly fastestLap as LapTime', () => {
      expect(result.fastestLap).toBeInstanceOf(LapTime);
      expect(result.fastestLap.toNumber()).toBe(validFastestLap);
    });

    it('should have readonly incidents as IncidentCount', () => {
      expect(result.incidents).toBeInstanceOf(IncidentCount);
      expect(result.incidents.toNumber()).toBe(validIncidents);
    });

    it('should have readonly startPosition as Position', () => {
      expect(result.startPosition).toBeInstanceOf(Position);
      expect(result.startPosition.toNumber()).toBe(validStartPosition);
    });
  });

  describe('domain methods', () => {
    it('should calculate position change correctly', () => {
      const result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: 1,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: 3,
      });

      expect(result.getPositionChange()).toBe(2); // 3 - 1
    });

    it('should return true for podium finish', () => {
      const result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: 3,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      });

      expect(result.isPodium()).toBe(true);
    });

    it('should return false for non-podium finish', () => {
      const result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: 4,
        fastestLap: validFastestLap,
        incidents: validIncidents,
        startPosition: validStartPosition,
      });

      expect(result.isPodium()).toBe(false);
    });

    it('should return true for clean race', () => {
      const result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: 0,
        startPosition: validStartPosition,
      });

      expect(result.isClean()).toBe(true);
    });

    it('should return false for race with incidents', () => {
      const result = Result.create({
        id: validId,
        raceId: validRaceId,
        driverId: validDriverId,
        position: validPosition,
        fastestLap: validFastestLap,
        incidents: 1,
        startPosition: validStartPosition,
      });

      expect(result.isClean()).toBe(false);
    });
  });
});