import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
  ListSeasonsForLeagueUseCase,
  type ListSeasonsForLeagueInput,
  type ListSeasonsForLeagueResult,
  type ListSeasonsForLeagueErrorCode,
} from './ListSeasonsForLeagueUseCase';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import { Season } from '../../domain/entities/season/Season';
import type { UseCaseOutputPort } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';


describe('ListSeasonsForLeagueUseCase', () => {
  let useCase: ListSeasonsForLeagueUseCase;
  let leagueRepository: {
    findById: Mock;
  };
  let seasonRepository: {
    listByLeague: Mock;
  };
  let output: UseCaseOutputPort<ListSeasonsForLeagueResult> & { present: Mock };

  beforeEach(() => {
    leagueRepository = {
      findById: vi.fn(),
    };
    seasonRepository = {
      listByLeague: vi.fn(),
    };
    output = { present: vi.fn() } as unknown as UseCaseOutputPort<
      ListSeasonsForLeagueResult
    > & { present: Mock };
    useCase = new ListSeasonsForLeagueUseCase(
      leagueRepository as unknown as ILeagueRepository,
      seasonRepository as unknown as ISeasonRepository,
      output,
    );
  });

  it('lists seasons for a league with domain entities and presents them via output port', async () => {
    const leagueId = 'league-1';
    const league = { id: leagueId };
    const seasons = [
      Season.create({
        id: 'season-1',
        leagueId,
        gameId: 'iracing',
        name: 'Season One',
        status: 'planned',
      }),
      Season.create({
        id: 'season-2',
        leagueId,
        gameId: 'iracing',
        name: 'Season Two',
        status: 'active',
      }),
    ];

    leagueRepository.findById.mockResolvedValue(league);
    seasonRepository.listByLeague.mockResolvedValue(seasons);

    const input: ListSeasonsForLeagueInput = { leagueId };

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(result.unwrap()).toBeUndefined();

    expect(output.present).toHaveBeenCalledTimes(1);
    const presented =
      output.present.mock.calls[0]?.[0] as ListSeasonsForLeagueResult;

    expect(presented.leagueId).toBe(leagueId);
    expect(presented.seasons).toHaveLength(2);
    const presentedIds = presented.seasons.map((s) => s.id).sort();
    expect(presentedIds).toEqual(['season-1', 'season-2']);

    const firstSeason = presented.seasons.find((s) => s.id === 'season-1');
    const secondSeason = presented.seasons.find((s) => s.id === 'season-2');

    expect(firstSeason?.name).toBe('Season One');
    expect(firstSeason?.status.toString()).toBe('planned');
    expect(secondSeason?.name).toBe('Season Two');
    expect(secondSeason?.status.toString()).toBe('active');
  });

  it('returns error when league not found and does not call output', async () => {
    leagueRepository.findById.mockResolvedValue(null);

    const input: ListSeasonsForLeagueInput = { leagueId: 'league-1' };

    const result = await useCase.execute(input);

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

    expect(output.present).not.toHaveBeenCalled();
  });

  it('wraps repository failures and does not call output', async () => {
    const leagueId = 'league-1';
    const league = { id: leagueId };
    const thrown = new Error('DB is down');

    leagueRepository.findById.mockResolvedValue(league);
    seasonRepository.listByLeague.mockRejectedValue(thrown);

    const input: ListSeasonsForLeagueInput = { leagueId };

    const result = await useCase.execute(input);

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

    expect(output.present).not.toHaveBeenCalled();
  });
});
