import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { StandingRepository } from '../../domain/repositories/StandingRepository';
import {
    ImportRaceResultsUseCase,
    type ImportRaceResultsErrorCode,
    type ImportRaceResultsInput
} from './ImportRaceResultsUseCase';

describe('ImportRaceResultsUseCase', () => {
  let useCase: ImportRaceResultsUseCase;
  let raceRepository: {
    findById: Mock;
  };
  let leagueRepository: {
    findById: Mock;
  };
  let resultRepository: {
    existsByRaceId: Mock;
    createMany: Mock;
  };
  let driverRepository: {
    findByIRacingId: Mock;
  };
  let standingRepository: {
    recalculate: Mock;
  };
  let logger: {
    debug: Mock;
    info: Mock;
    warn: Mock;
    error: Mock;
  };
  beforeEach(() => {
    raceRepository = {
      findById: vi.fn(),
    };
    leagueRepository = {
      findById: vi.fn(),
    };
    resultRepository = {
      existsByRaceId: vi.fn(),
      createMany: vi.fn(),
    };
    driverRepository = {
      findByIRacingId: vi.fn(),
    };
    standingRepository = {
      recalculate: vi.fn(),
    };
    logger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
    useCase = new ImportRaceResultsUseCase(raceRepository as unknown as RaceRepository,
      leagueRepository as unknown as LeagueRepository,
      resultRepository as unknown as ResultRepository,
      driverRepository as unknown as DriverRepository,
      standingRepository as unknown as StandingRepository,
      logger as unknown as Logger);
  });

  it('should return race not found error', async () => {
    const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };

    raceRepository.findById.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      ImportRaceResultsErrorCode,
      { message: string }
    >;
    expect(error).toEqual({
      code: 'RACE_NOT_FOUND',
      details: { message: 'Race race-1 not found' },
    });
  });

  it('should return league not found error', async () => {
    const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };

    raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
    leagueRepository.findById.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      ImportRaceResultsErrorCode,
      { message: string }
    >;
    expect(error).toEqual({
      code: 'LEAGUE_NOT_FOUND',
      details: { message: 'League league-1 not found' },
    });
  });

  it('should return results exist error', async () => {
    const input: ImportRaceResultsInput = { raceId: 'race-1', rows: [] };

    raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
    leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
    resultRepository.existsByRaceId.mockResolvedValue(true);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      ImportRaceResultsErrorCode,
      { message: string }
    >;
    expect(error).toEqual({
      code: 'RESULTS_EXIST',
      details: { message: 'Results already exist for this race' },
    });
  });

  it('should return driver not found error', async () => {
    const input: ImportRaceResultsInput = {
      raceId: 'race-1',
      rows: [
        {
          id: 'result-1',
          driverExternalId: '123',
          position: 1,
          fastestLap: 100,
          incidents: 0,
          startPosition: 1,
        },
      ],
    };

    raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
    leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
    resultRepository.existsByRaceId.mockResolvedValue(false);
    driverRepository.findByIRacingId.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      ImportRaceResultsErrorCode,
      { message: string }
    >;
    expect(error).toEqual({
      code: 'DRIVER_NOT_FOUND',
      details: { message: 'Driver with iRacing ID 123 not found' },
    });
  });

  it('should import results successfully', async () => {
    const input: ImportRaceResultsInput = {
      raceId: 'race-1',
      rows: [
        {
          id: 'result-1',
          driverExternalId: '123',
          position: 1,
          fastestLap: 100,
          incidents: 0,
          startPosition: 1,
        },
      ],
    };

    raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
    leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
    resultRepository.existsByRaceId.mockResolvedValue(false);
    driverRepository.findByIRacingId.mockResolvedValue({ id: 'driver-1' });
    resultRepository.createMany.mockResolvedValue(undefined);
    standingRepository.recalculate.mockResolvedValue(undefined);

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    const value = result.unwrap();
    expect(value).toEqual({
      raceId: 'race-1',
      leagueId: 'league-1',
      driversProcessed: 1,
      resultsRecorded: 1,
      errors: [],
    });
  });

  it('should return repository error when persistence fails', async () => {
    const input: ImportRaceResultsInput = {
      raceId: 'race-1',
      rows: [
        {
          id: 'result-1',
          driverExternalId: '123',
          position: 1,
          fastestLap: 100,
          incidents: 0,
          startPosition: 1,
        },
      ],
    };

    raceRepository.findById.mockResolvedValue({ id: 'race-1', leagueId: 'league-1' });
    leagueRepository.findById.mockResolvedValue({ id: 'league-1' });
    resultRepository.existsByRaceId.mockResolvedValue(false);
    driverRepository.findByIRacingId.mockResolvedValue({ id: 'driver-1' });
    resultRepository.createMany.mockRejectedValue(new Error('DB failure'));

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      ImportRaceResultsErrorCode,
      { message: string }
    >;
    expect(error.code).toBe('REPOSITORY_ERROR');
    expect(error.details.message).toBe('DB failure');
  });
});