import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import { League } from '../../domain/entities/League';
import { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
import { Season } from '../../domain/entities/season/Season';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { BonusRule } from '../../domain/types/BonusRule';
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
import type { SessionType } from '../../domain/types/SessionType';
import {
    LeagueVisibility,
    MIN_RANKED_LEAGUE_DRIVERS,
} from '../../domain/value-objects/LeagueVisibility';
import { PointsTable } from '../../domain/value-objects/PointsTable';

export type CreateLeagueWithSeasonAndScoringCommand = {
  name: string;
  description?: string;
  /**
   * League visibility/ranking mode.
   * - 'ranked' (or legacy 'public'): Competitive, public, affects ratings. Requires min 10 drivers.
   * - 'unranked' (or legacy 'private'): Casual with friends, no rating impact.
   */
  visibility: string;
  ownerId: string;
  gameId: string;
  maxDrivers?: number;
  maxTeams?: number;
  enableDriverChampionship: boolean;
  enableTeamChampionship: boolean;
  enableNationsChampionship: boolean;
  enableTrophyChampionship: boolean;
  scoringPresetId?: string;
};

export type CreateLeagueWithSeasonAndScoringResult = {
  league: League;
  season: Season;
  scoringConfig: LeagueScoringConfig;
};

type CreateLeagueWithSeasonAndScoringErrorCode =
  | 'VALIDATION_ERROR'
  | 'UNKNOWN_PRESET'
  | 'REPOSITORY_ERROR';

type ScoringPreset = {
  id: string;
  name: string;
};

export class CreateLeagueWithSeasonAndScoringUseCase {
  constructor(
    private readonly leagueRepository: LeagueRepository,
    private readonly seasonRepository: SeasonRepository,
    private readonly leagueScoringConfigRepository: LeagueScoringConfigRepository,
    private readonly getLeagueScoringPresetById: (input: { presetId: string }) => Promise<ScoringPreset | undefined>,
    private readonly logger: Logger,
  ) {}

  async execute(
    command: CreateLeagueWithSeasonAndScoringCommand,
  ): Promise<
    Result<
      CreateLeagueWithSeasonAndScoringResult,
      ApplicationErrorCode<CreateLeagueWithSeasonAndScoringErrorCode>
    >
  > {
    this.logger.debug('Executing CreateLeagueWithSeasonAndScoringUseCase', { command });
    const validation = this.validate(command);
    if (validation.isErr()) {
      return Result.err(validation.unwrapErr());
    }
    this.logger.info('Command validated successfully.');
    try {

      const leagueId = uuidv4();
      this.logger.debug(`Generated leagueId: ${leagueId}`);

      const league = League.create({
        id: leagueId,
        name: command.name,
        description: command.description ?? '',
        ownerId: command.ownerId,
        settings: {
          pointsSystem: 'custom',
          ...(command.maxDrivers !== undefined ? { maxDrivers: command.maxDrivers } : {}),
        },
      });

      await this.leagueRepository.create(league);
      this.logger.info(`League ${league.name} (${league.id}) created successfully.`);

      const seasonId = uuidv4();
      this.logger.debug(`Generated seasonId: ${seasonId}`);
      const season = Season.create({
        id: seasonId,
        leagueId: league.id.toString(),
        gameId: command.gameId,
        name: `${command.name} Season 1`,
        year: new Date().getFullYear(),
        order: 1,
        status: 'active',
        startDate: new Date(),
        endDate: new Date(),
      });

      await this.seasonRepository.create(season);
      this.logger.info(`Season ${season.name} (${season.id}) created for league ${league.id}.`);

      const presetId = command.scoringPresetId ?? 'club-default';
      this.logger.debug(`Attempting to retrieve scoring preset: ${presetId}`);
      const preset = await this.getLeagueScoringPresetById({ presetId });

      if (!preset) {
        this.logger.error(`Unknown scoring preset: ${presetId}`);
        return Result.err({ code: 'UNKNOWN_PRESET', details: { message: `Unknown scoring preset: ${presetId}` } });
      }
      this.logger.info(`Scoring preset ${preset.name} (${preset.id}) retrieved.`);

      const championships = this.createDefaultChampionshipConfigs(command);
      const scoringConfig = LeagueScoringConfig.create({
        seasonId,
        scoringPresetId: preset.id,
        championships,
      });
      this.logger.debug(`Scoring configuration created from preset ${preset.id}.`);

      await this.leagueScoringConfigRepository.save(scoringConfig);
      this.logger.info(`Scoring configuration saved for season ${seasonId}.`);

      const result: CreateLeagueWithSeasonAndScoringResult = {
        league,
        season,
        scoringConfig,
      };
      this.logger.debug('CreateLeagueWithSeasonAndScoringUseCase completed successfully.', { result });
      
      return Result.ok(result);
    } catch (error) {
      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: {
          message: error instanceof Error ? error.message : 'Unknown error',
        },
      });
    }
  }

  private createDefaultChampionshipConfigs(
    command: CreateLeagueWithSeasonAndScoringCommand,
  ): ChampionshipConfig[] {
    const sessionTypes: SessionType[] = ['main'];

    const defaultPoints = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1];
    const pointsMap: Record<number, number> = {};
    defaultPoints.forEach((points, index) => {
      pointsMap[index + 1] = points;
    });

    const pointsTableBySessionType: Record<SessionType, PointsTable> =
      {} as Record<SessionType, PointsTable>;
    const bonusRulesBySessionType: Record<SessionType, BonusRule[]> =
      {} as Record<SessionType, BonusRule[]>;

    for (const sessionType of sessionTypes) {
      pointsTableBySessionType[sessionType] = new PointsTable(pointsMap);
      bonusRulesBySessionType[sessionType] = [];
    }

    const configs: ChampionshipConfig[] = [];

    if (command.enableDriverChampionship) {
      configs.push({
        id: uuidv4(),
        name: 'Driver Championship',
        type: 'driver',
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy: { strategy: 'none' },
      });
    }

    if (command.enableTeamChampionship) {
      configs.push({
        id: uuidv4(),
        name: 'Team Championship',
        type: 'team',
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy: { strategy: 'none' },
      });
    }

    if (command.enableNationsChampionship) {
      configs.push({
        id: uuidv4(),
        name: 'Nations Championship',
        type: 'nations',
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy: { strategy: 'none' },
      });
    }

    if (command.enableTrophyChampionship) {
      configs.push({
        id: uuidv4(),
        name: 'Trophy Championship',
        type: 'trophy',
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy: { strategy: 'none' },
      });
    }

    if (configs.length === 0) {
      configs.push({
        id: uuidv4(),
        name: 'Driver Championship',
        type: 'driver',
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy: { strategy: 'none' },
      });
    }

    return configs;
  }

  private validate(
    command: CreateLeagueWithSeasonAndScoringCommand,
  ): Result<void, ApplicationErrorCode<'VALIDATION_ERROR', { message: string }>> {
    this.logger.debug('Validating CreateLeagueWithSeasonAndScoringCommand', { command });
    if (!command.name || command.name.trim().length === 0) {
      this.logger.warn('Validation failed: League name is required', { command });
      return Result.err({ code: 'VALIDATION_ERROR', details: { message: 'League name is required' } });
    }
    if (!command.ownerId || command.ownerId.trim().length === 0) {
      this.logger.warn('Validation failed: League ownerId is required', { command });
      return Result.err({ code: 'VALIDATION_ERROR', details: { message: 'League ownerId is required' } });
    }
    if (!command.gameId || command.gameId.trim().length === 0) {
      this.logger.warn('Validation failed: gameId is required', { command });
      return Result.err({ code: 'VALIDATION_ERROR', details: { message: 'gameId is required' } });
    }
    if (!command.visibility) {
      this.logger.warn('Validation failed: visibility is required', { command });
      return Result.err({ code: 'VALIDATION_ERROR', details: { message: 'visibility is required' } });
    }
    if (command.maxDrivers !== undefined && command.maxDrivers <= 0) {
      this.logger.warn('Validation failed: maxDrivers must be greater than 0 when provided', { command });
      return Result.err({ code: 'VALIDATION_ERROR', details: { message: 'maxDrivers must be greater than 0 when provided' } });
    }

    const visibility = LeagueVisibility.fromString(command.visibility);

    if (visibility.isRanked()) {
      const driverCount = command.maxDrivers ?? 0;
      if (driverCount < MIN_RANKED_LEAGUE_DRIVERS) {
        this.logger.warn(
          `Validation failed: Ranked leagues require at least ${MIN_RANKED_LEAGUE_DRIVERS} drivers. Current setting: ${driverCount}.`,
          { command }
        );
        return Result.err({ code: 'VALIDATION_ERROR', details: { message:
          `Ranked leagues require at least ${MIN_RANKED_LEAGUE_DRIVERS} drivers. ` +
          `Current setting: ${driverCount}. ` +
          `For smaller groups, consider creating an Unranked (Friends) league instead.`
        } });
      }
    }
    this.logger.debug('Validation successful.');
    return Result.ok(undefined);
  }
}