import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { ImportRaceResultsApiUseCase } from './ImportRaceResultsApiUseCase';
import { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import { IResultRepository } from '../../domain/repositories/IResultRepository';
import { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { Logger } from '@core/shared/application';

describe('ImportRaceResultsApiUseCase', () => {
  let useCase: ImportRaceResultsApiUseCase;
  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 ImportRaceResultsApiUseCase(
      raceRepository as unknown as IRaceRepository,
      leagueRepository as unknown as ILeagueRepository,
      resultRepository as unknown as IResultRepository,
      driverRepository as unknown as IDriverRepository,
      standingRepository as unknown as IStandingRepository,
      logger as unknown as Logger,
    );
  });

  it('should return parse error for invalid JSON', async () => {
    const params = { raceId: 'race-1', resultsFileContent: 'invalid json' };

    const result = await useCase.execute(params);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'PARSE_ERROR',
      details: { message: 'Invalid JSON in results file content' },
    });
  });

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

    raceRepository.findById.mockResolvedValue(null);

    const result = await useCase.execute(params);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'RACE_NOT_FOUND',
      details: { message: 'Race race-1 not found' },
    });
  });

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

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

    const result = await useCase.execute(params);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'LEAGUE_NOT_FOUND',
      details: { message: 'League league-1 not found' },
    });
  });

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

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

    const result = await useCase.execute(params);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'RESULTS_EXIST',
      details: { message: 'Results already exist for this race' },
    });
  });

  it('should return driver not found error', async () => {
    const params = { raceId: 'race-1', resultsFileContent: '[{"id":"result-1","raceId":"race-1","driverId":"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(params);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'DRIVER_NOT_FOUND',
      details: { message: 'Driver with iRacing ID 123 not found' },
    });
  });

  it('should import results successfully', async () => {
    const params = { raceId: 'race-1', resultsFileContent: '[{"id":"result-1","raceId":"race-1","driverId":"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(params);

    expect(result.isOk()).toBe(true);
    expect(result.unwrap()).toEqual({
      success: true,
      raceId: 'race-1',
      driversProcessed: 1,
      resultsRecorded: 1,
      errors: [],
    });
    expect(resultRepository.createMany).toHaveBeenCalledWith([
      expect.objectContaining({
        id: 'result-1',
        raceId: 'race-1',
        driverId: 'driver-1',
        position: 1,
        fastestLap: 100,
        incidents: 0,
        startPosition: 1,
      }),
    ]);
    expect(standingRepository.recalculate).toHaveBeenCalledWith('league-1');
  });
});