import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
  ListLeagueScoringPresetsUseCase,
  type ListLeagueScoringPresetsInput,
  type ListLeagueScoringPresetsResult,
  type ListLeagueScoringPresetsErrorCode,
} from './ListLeagueScoringPresetsUseCase';
import type { UseCaseOutputPort } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Result } from '@core/shared/application/Result';
 
describe('ListLeagueScoringPresetsUseCase', () => {
  let useCase: ListLeagueScoringPresetsUseCase;
  let output: UseCaseOutputPort<ListLeagueScoringPresetsResult> & { present: Mock };
 
  beforeEach(() => {
    const mockPresets = [
      {
        id: 'preset-1',
        name: 'Preset 1',
        description: 'Desc 1',
        primaryChampionshipType: 'driver' as const,
        dropPolicySummary: 'Drop 1',
        sessionSummary: 'Session 1',
        bonusSummary: 'Bonus 1',
        defaultTimings: {
          practiceMinutes: 15,
          qualifyingMinutes: 10,
          sprintRaceMinutes: 20,
          mainRaceMinutes: 30,
          sessionCount: 2,
        },
        createConfig: vi.fn(),
      },
      {
        id: 'preset-2',
        name: 'Preset 2',
        description: 'Desc 2',
        primaryChampionshipType: 'team' as const,
        dropPolicySummary: 'Drop 2',
        sessionSummary: 'Session 2',
        bonusSummary: 'Bonus 2',
        defaultTimings: {
          practiceMinutes: 20,
          qualifyingMinutes: 15,
          sprintRaceMinutes: 25,
          mainRaceMinutes: 40,
          sessionCount: 3,
        },
        createConfig: vi.fn(),
      },
    ];
 
    output = {
      present: vi.fn(),
    } as unknown as UseCaseOutputPort<ListLeagueScoringPresetsResult> & { present: Mock };
 
    useCase = new ListLeagueScoringPresetsUseCase(mockPresets, output);
  });
 
  it('should list presets successfully', async () => {
    const input: ListLeagueScoringPresetsInput = {};

    const result: Result<
      void,
      ApplicationErrorCode<ListLeagueScoringPresetsErrorCode, { message: string }>
    > = await useCase.execute(input);
 
    expect(result.isOk()).toBe(true);
    expect(result.unwrap()).toBeUndefined();
    expect(output.present).toHaveBeenCalledTimes(1);

    const firstCall = output.present.mock.calls[0]!;
    const presented = firstCall[0] as ListLeagueScoringPresetsResult;

    expect(presented).toEqual({
      presets: [
        {
          id: 'preset-1',
          name: 'Preset 1',
          description: 'Desc 1',
          primaryChampionshipType: 'driver',
          sessionSummary: 'Session 1',
          bonusSummary: 'Bonus 1',
          dropPolicySummary: 'Drop 1',
          defaultTimings: {
            practiceMinutes: 15,
            qualifyingMinutes: 10,
            sprintRaceMinutes: 20,
            mainRaceMinutes: 30,
            sessionCount: 2,
          },
        },
        {
          id: 'preset-2',
          name: 'Preset 2',
          description: 'Desc 2',
          primaryChampionshipType: 'team',
          sessionSummary: 'Session 2',
          bonusSummary: 'Bonus 2',
          dropPolicySummary: 'Drop 2',
          defaultTimings: {
            practiceMinutes: 20,
            qualifyingMinutes: 15,
            sprintRaceMinutes: 25,
            mainRaceMinutes: 40,
            sessionCount: 3,
          },
        },
      ],
    });
  });

  it('should wrap repository errors in ApplicationErrorCode and not call output', async () => {
    const failingPresets = {
      map: () => {
        throw new Error('Repository failure');
      },
    } as unknown as never[];

    useCase = new ListLeagueScoringPresetsUseCase(
      failingPresets,
      output,
    );

    const input: ListLeagueScoringPresetsInput = {};

    const result: Result<
      void,
      ApplicationErrorCode<ListLeagueScoringPresetsErrorCode, { message: string }>
    > = await useCase.execute(input);

    expect(result.isErr()).toBe(true);

    const error = result.unwrapErr() as ApplicationErrorCode<
      ListLeagueScoringPresetsErrorCode,
      { message: string }
    >;

    expect(error.code).toBe('REPOSITORY_ERROR');
    expect(error.details?.message).toBe('Repository failure');
    expect(output.present).not.toHaveBeenCalled();
  });
});