import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import {
    GetAllRacesPageDataUseCase,
    type GetAllRacesPageDataInput
} from './GetAllRacesPageDataUseCase';

import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';

describe('GetAllRacesPageDataUseCase', () => {
  const mockRaceFindAll = vi.fn();
  const mockRaceRepo: {
    findById: Mock;
    findAll: Mock;
    findByLeagueId: Mock;
    findUpcomingByLeagueId: Mock;
    findCompletedByLeagueId: Mock;
    findByStatus: Mock;
    findByDateRange: Mock;
    create: Mock;
    update: Mock;
    delete: Mock;
    exists: Mock;
  } = {
    findById: vi.fn(),
    findAll: mockRaceFindAll,
    findByLeagueId: vi.fn(),
    findUpcomingByLeagueId: vi.fn(),
    findCompletedByLeagueId: vi.fn(),
    findByStatus: vi.fn(),
    findByDateRange: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    exists: vi.fn(),
  };

  const mockLeagueFindAll = vi.fn();
  const mockLeagueRepo: {
    findById: Mock;
    findAll: Mock;
    findByOwnerId: Mock;
    create: Mock;
    update: Mock;
    delete: Mock;
    exists: Mock;
    searchByName: Mock;
  } = {
    findById: vi.fn(),
    findAll: mockLeagueFindAll,
    findByOwnerId: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    exists: vi.fn(),
    searchByName: vi.fn(),
  };

  const mockLogger: Logger = {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should return races and filters data', async () => {
    const useCase = new GetAllRacesPageDataUseCase(
      mockRaceRepo as unknown as RaceRepository,
      mockLeagueRepo as unknown as LeagueRepository,
      mockLogger
    );

    const race1 = Race.create({
      id: 'race1',
      leagueId: 'league1',
      track: 'Track A',
      car: 'Car A',
      scheduledAt: new Date('2023-01-01T10:00:00Z'),
      status: 'scheduled',
      strengthOfField: 5,
    });

    const race2 = Race.create({
      id: 'race2',
      leagueId: 'league2',
      track: 'Track B',
      car: 'Car B',
      scheduledAt: new Date('2023-01-02T10:00:00Z'),
      status: 'completed',
    });

    const league1 = League.create({
      id: 'league1',
      name: 'League One',
      description: 'League One',
      ownerId: 'owner-1',
    });

    const league2 = League.create({
      id: 'league2',
      name: 'League Two',
      description: 'League Two',
      ownerId: 'owner-2',
    });

    mockRaceFindAll.mockResolvedValue([race1, race2]);
    mockLeagueFindAll.mockResolvedValue([league1, league2]);

    const input: GetAllRacesPageDataInput = {};

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();
    expect(presented.races).toEqual([
      {
        id: 'race2',
        track: 'Track B',
        car: 'Car B',
        scheduledAt: '2023-01-02T10:00:00.000Z',
        status: 'completed',
        leagueId: 'league2',
        leagueName: 'League Two',
        strengthOfField: null,
      },
      {
        id: 'race1',
        track: 'Track A',
        car: 'Car A',
        scheduledAt: '2023-01-01T10:00:00.000Z',
        status: 'scheduled',
        leagueId: 'league1',
        leagueName: 'League One',
        strengthOfField: 5,
      },
    ]);

    expect(presented.filters).toEqual({
      statuses: [
        { value: 'all', label: 'All Statuses' },
        { value: 'scheduled', label: 'Scheduled' },
        { value: 'running', label: 'Live' },
        { value: 'completed', label: 'Completed' },
        { value: 'cancelled', label: 'Cancelled' },
      ],
      leagues: [
        { id: 'league1', name: 'League One' },
        { id: 'league2', name: 'League Two' },
      ],
    });
  });

  it('should return empty result when no races or leagues', async () => {
    const useCase = new GetAllRacesPageDataUseCase(
      mockRaceRepo as unknown as RaceRepository,
      mockLeagueRepo as unknown as LeagueRepository,
      mockLogger
    );

    mockRaceFindAll.mockResolvedValue([]);
    mockLeagueFindAll.mockResolvedValue([]);

    const result = await useCase.execute({});

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();
    expect(presented.races).toEqual([]);
    expect(presented.filters).toEqual({
      statuses: [
        { value: 'all', label: 'All Statuses' },
        { value: 'scheduled', label: 'Scheduled' },
        { value: 'running', label: 'Live' },
        { value: 'completed', label: 'Completed' },
        { value: 'cancelled', label: 'Cancelled' },
      ],
      leagues: [],
    });
  });

  it('should return error when repository throws', async () => {
    const useCase = new GetAllRacesPageDataUseCase(
      mockRaceRepo as unknown as RaceRepository,
      mockLeagueRepo as unknown as LeagueRepository,
      mockLogger
    );

    const error = new Error('Repository error');
    mockRaceFindAll.mockRejectedValue(error);

    const result = await useCase.execute({});

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr();
    expect(err.code).toBe('REPOSITORY_ERROR');
    expect(err.details.message).toBe('Repository error');
  });
});
