import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { GetLeagueScoringConfigResult } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { BonusRule } from '@core/racing/domain/types/BonusRule';

export interface LeagueScoringChampionshipViewModel {
  id: string;
  name: string;
  type: string;
  sessionTypes: string[];
  pointsPreview: Array<{ sessionType: string; position: number; points: number }>;
  bonusSummary: string[];
  dropPolicyDescription: string;
}

export interface LeagueScoringConfigViewModel {
  leagueId: string;
  seasonId: string;
  gameId: string;
  gameName: string;
  scoringPresetId?: string;
  scoringPresetName?: string;
  dropPolicySummary: string;
  championships: LeagueScoringChampionshipViewModel[];
}

export class LeagueScoringConfigPresenter implements UseCaseOutputPort<GetLeagueScoringConfigResult> {
  private viewModel: LeagueScoringConfigViewModel | null = null;

  reset(): void {
    this.viewModel = null;
  }

  present(result: GetLeagueScoringConfigResult): void {
    const championships: LeagueScoringChampionshipViewModel[] =
      result.scoringConfig.championships.map((champ) => this.mapChampionship(champ));

    const dropPolicySummary =
      result.preset?.dropPolicySummary ??
      this.deriveDropPolicyDescriptionFromChampionships(result.scoringConfig.championships);

    this.viewModel = {
      leagueId: result.league.id.toString(),
      seasonId: result.season.id,
      gameId: result.game.id.toString(),
      gameName: result.game.name.toString(),
      scoringPresetId: result.scoringConfig.scoringPresetId?.toString() ?? 'custom',
      scoringPresetName: result.preset?.name ?? 'Custom',
      dropPolicySummary,
      championships,
    };
  }

  getViewModel(): LeagueScoringConfigViewModel | null {
    return this.viewModel;
  }

  private mapChampionship(championship: ChampionshipConfig): LeagueScoringChampionshipViewModel {
    const sessionTypes = championship.sessionTypes.map((s) => s.toString());
    const pointsPreview = this.buildPointsPreview(championship.pointsTableBySessionType);
    const bonusSummary = this.buildBonusSummary(
      championship.bonusRulesBySessionType ?? {},
    );
    const dropPolicyDescription = this.deriveDropPolicyDescription(
      championship.dropScorePolicy,
    );

    return {
      id: championship.id,
      name: championship.name,
      type: championship.type,
      sessionTypes,
      pointsPreview,
      bonusSummary,
      dropPolicyDescription,
    };
  }

  private buildPointsPreview(
    tables: Record<string, { getPointsForPosition: (position: number) => number }>,
  ): Array<{ sessionType: string; position: number; points: number }> {
    const preview: Array<{
      sessionType: string;
      position: number;
      points: number;
    }> = [];

    const maxPositions = 10;

    for (const [sessionType, table] of Object.entries(tables)) {
      for (let pos = 1; pos <= maxPositions; pos++) {
        const points = table.getPointsForPosition(pos);
        if (points && points !== 0) {
          preview.push({
            sessionType,
            position: pos,
            points,
          });
        }
      }
    }

    return preview;
  }

  private buildBonusSummary(
    bonusRulesBySessionType: Record<string, BonusRule[]>,
  ): string[] {
    const summaries: string[] = [];

    for (const [sessionType, rules] of Object.entries(bonusRulesBySessionType)) {
      for (const rule of rules) {
        if (rule.type === 'fastestLap') {
          const base = `Fastest lap in ${sessionType}`;
          if (rule.requiresFinishInTopN) {
            summaries.push(
              `${base} +${rule.points} points if finishing P${rule.requiresFinishInTopN} or better`,
            );
          } else {
            summaries.push(`${base} +${rule.points} points`);
          }
        } else {
          summaries.push(
            `${rule.type} bonus in ${sessionType} worth ${rule.points} points`,
          );
        }
      }
    }

    return summaries;
  }

  private deriveDropPolicyDescriptionFromChampionships(
    championships: ChampionshipConfig[],
  ): string {
    const first = championships[0];
    if (!first) {
      return 'All results count';
    }
    return this.deriveDropPolicyDescription(first.dropScorePolicy);
  }

  private deriveDropPolicyDescription(policy: {
    strategy: string;
    count?: number;
    dropCount?: number;
  }): string {
    if (!policy || policy.strategy === 'none') {
      return 'All results count';
    }

    if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') {
      return `Best ${policy.count} results count towards the championship`;
    }

    if (
      policy.strategy === 'dropWorstN' &&
      typeof policy.dropCount === 'number'
    ) {
      return `Worst ${policy.dropCount} results are dropped from the championship total`;
    }

    return 'Custom drop score rules apply';
  }
}