import {
    CreateSeasonForLeagueUseCase,
    type CreateSeasonForLeagueInput,
    type LeagueConfigFormModel,
} from '@core/racing/application/use-cases/CreateSeasonForLeagueUseCase';
import { Season } from '@core/racing/domain/entities/season/Season';
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';

function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>): LeagueConfigFormModel {
  return {
    basics: {
      name: 'Test League',
      visibility: 'ranked',
      gameId: 'iracing',
      ...overrides?.basics,
    },
    structure: {
      mode: 'solo',
      maxDrivers: 30,
      ...overrides?.structure,
    },
    championships: {
      enableDriverChampionship: true,
      enableTeamChampionship: false,
      enableNationsChampionship: false,
      enableTrophyChampionship: false,
      ...overrides?.championships,
    },
    scoring: {
      patternId: 'sprint-main-driver',
      customScoringEnabled: false,
      ...overrides?.scoring,
    },
    dropPolicy: {
      strategy: 'bestNResults',
      n: 3,
      ...overrides?.dropPolicy,
    },
    timings: {
      qualifyingMinutes: 10,
      mainRaceMinutes: 30,
      sessionCount: 8,
      seasonStartDate: '2025-01-01',
      raceStartTime: '20:00',
      timezoneId: 'UTC',
      recurrenceStrategy: 'weekly',
      weekdays: ['Mon'],
      ...overrides?.timings,
    },
    stewarding: {
      decisionMode: 'steward_vote',
      requiredVotes: 3,
      requireDefense: true,
      defenseTimeLimit: 24,
      voteTimeLimit: 24,
      protestDeadlineHours: 48,
      stewardingClosesHours: 72,
      notifyAccusedOnProtest: true,
      notifyOnVoteRequired: true,
      ...overrides?.stewarding,
    },
    ...overrides,
  };
}

describe('CreateSeasonForLeagueUseCase', () => {
  const mockLeagueFindById = vi.fn();
  const mockLeagueRepo: {
    findById: Mock;
    findAll: Mock;
    findByOwnerId: Mock;
    create: Mock;
    update: Mock;
    delete: Mock;
    exists: Mock;
    searchByName: Mock;
  } = {
    findById: mockLeagueFindById,
    findAll: vi.fn(),
    findByOwnerId: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    exists: vi.fn(),
    searchByName: vi.fn(),
  };

  const mockSeasonFindById = vi.fn();
  const mockSeasonAdd = vi.fn();
  const mockSeasonRepo: {
    findById: Mock;
    findByLeagueId: Mock;
    create: Mock;
    add: Mock;
    update: Mock;
    listByLeague: Mock;
    listActiveByLeague: Mock;
  } = {
    findById: mockSeasonFindById,
    findByLeagueId: vi.fn(),
    create: vi.fn(),
    add: mockSeasonAdd,
    update: vi.fn(),
    listByLeague: vi.fn(),
    listActiveByLeague: vi.fn(),
  };

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

  it('creates a planned Season for an existing league with config-derived props', async () => {
    mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
    mockSeasonAdd.mockResolvedValue(undefined);

    const useCase = new CreateSeasonForLeagueUseCase(
      mockLeagueRepo as unknown as LeagueRepository, 
      mockSeasonRepo as unknown as SeasonRepository
    );

    const config = createLeagueConfigFormModel({
      basics: {
        name: 'League With Config',
        visibility: 'ranked',
        gameId: 'iracing',
      },
      scoring: {
        patternId: 'club-default',
        customScoringEnabled: true,
      },
      dropPolicy: {
        strategy: 'dropWorstN',
        n: 2,
      },
      timings: {
        qualifyingMinutes: 10,
        mainRaceMinutes: 30,
        sessionCount: 8,
      },
    });

    const command: CreateSeasonForLeagueInput = {
      leagueId: 'league-1',
      name: 'Season from Config',
      gameId: 'iracing',
      config,
    };

    const result = await useCase.execute(command);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();

    expect(presented.season).toBeInstanceOf(Season);
    expect(presented.league.id).toBe('league-1');
  });

  it('clones configuration from a source season when sourceSeasonId is provided', async () => {
    const sourceSeason = Season.create({
      id: 'source-season',
      leagueId: 'league-1',
      gameId: 'iracing',
      name: 'Source Season',
      status: 'planned',
    }).withMaxDrivers(40);

    mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
    mockSeasonFindById.mockResolvedValue(sourceSeason);
    mockSeasonAdd.mockResolvedValue(undefined);

    const useCase = new CreateSeasonForLeagueUseCase(
      mockLeagueRepo as unknown as LeagueRepository, 
      mockSeasonRepo as unknown as SeasonRepository
    );

    const command: CreateSeasonForLeagueInput = {
      leagueId: 'league-1',
      name: 'Cloned Season',
      gameId: 'iracing',
      sourceSeasonId: 'source-season',
    };

    const result = await useCase.execute(command);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();

    expect(presented.season.maxDrivers).toBe(40);
  });

  it('returns error when league not found', async () => {
    mockLeagueFindById.mockResolvedValue(null);

    const useCase = new CreateSeasonForLeagueUseCase(
      mockLeagueRepo as unknown as LeagueRepository, 
      mockSeasonRepo as unknown as SeasonRepository
    );

    const command: CreateSeasonForLeagueInput = {
      leagueId: 'missing-league',
      name: 'Any',
      gameId: 'iracing',
    };

    const result = await useCase.execute(command);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr();
    expect(error.code).toBe('LEAGUE_NOT_FOUND');
    expect(error.details?.message).toBe('League not found: missing-league');
  });

  it('returns validation error when source season is missing', async () => {
    mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
    mockSeasonFindById.mockResolvedValue(undefined);

    const useCase = new CreateSeasonForLeagueUseCase(
      mockLeagueRepo as unknown as LeagueRepository, 
      mockSeasonRepo as unknown as SeasonRepository
    );

    const command: CreateSeasonForLeagueInput = {
      leagueId: 'league-1',
      name: 'Cloned Season',
      gameId: 'iracing',
      sourceSeasonId: 'missing-source',
    };

    const result = await useCase.execute(command);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr();
    expect(error.code).toBe('VALIDATION_ERROR');
    expect(error.details?.message).toBe('Source Season not found: missing-source');
  });
});
