import { LeagueScoringConfig } from '@core/racing/domain/entities/LeagueScoringConfig';
import type { BonusRule } from '@core/racing/domain/types/BonusRule';
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { ChampionshipType } from '@core/racing/domain/types/ChampionshipType';
import type { DropScorePolicy } from '@core/racing/domain/types/DropScorePolicy';
import type { SessionType } from '@core/racing/domain/types/SessionType';
import { PointsTable } from '@core/racing/domain/value-objects/PointsTable';

export type LeagueScoringPresetPrimaryChampionshipType =
  | 'driver'
  | 'team'
  | 'nations'
  | 'trophy';

export interface LeagueScoringPresetTimingDefaults {
  practiceMinutes: number;
  qualifyingMinutes: number;
  sprintRaceMinutes: number;
  mainRaceMinutes: number;
  sessionCount: number;
}

export interface LeagueScoringPreset {
  id: string;
  name: string;
  description: string;
  primaryChampionshipType: LeagueScoringPresetPrimaryChampionshipType;
  dropPolicySummary: string;
  sessionSummary: string;
  bonusSummary: string;
  defaultTimings: LeagueScoringPresetTimingDefaults;
  createConfig: (options: { seasonId: string }) => LeagueScoringConfig;
}

const mainPointsSprintMain = new PointsTable({
  1: 25,
  2: 18,
  3: 15,
  4: 12,
  5: 10,
  6: 8,
  7: 6,
  8: 4,
  9: 2,
  10: 1,
});

const sprintPointsSprintMain = new PointsTable({
  1: 8,
  2: 7,
  3: 6,
  4: 5,
  5: 4,
  6: 3,
  7: 2,
  8: 1,
});

const clubMainPoints = new PointsTable({
  1: 20,
  2: 15,
  3: 12,
  4: 10,
  5: 8,
  6: 6,
  7: 4,
  8: 2,
  9: 1,
});

const enduranceMainPoints = new PointsTable({
  1: 50,
  2: 36,
  3: 30,
  4: 24,
  5: 20,
  6: 16,
  7: 12,
  8: 8,
  9: 4,
  10: 2,
});

export const leagueScoringPresets: LeagueScoringPreset[] = [
  {
    id: 'sprint-main-driver',
    name: 'Sprint + Main',
    description:
      'Short sprint race plus main race; sprint gives fewer points.',
    primaryChampionshipType: 'driver',
    dropPolicySummary: 'Best 6 results of 8 count towards the championship.',
    sessionSummary: 'Sprint + Main',
    bonusSummary: 'Fastest lap +1 point in main race if finishing P10 or better.',
    defaultTimings: {
      practiceMinutes: 20,
      qualifyingMinutes: 30,
      sprintRaceMinutes: 20,
      mainRaceMinutes: 40,
      sessionCount: 2,
    },
    createConfig: ({ seasonId }) => {
      const fastestLapBonus: BonusRule = {
        id: 'fastest-lap-main',
        type: 'fastestLap',
        points: 1,
        requiresFinishInTopN: 10,
      };

      const sessionTypes: SessionType[] = ['sprint', 'main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: sprintPointsSprintMain,
        main: mainPointsSprintMain,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const bonusRulesBySessionType: Record<SessionType, BonusRule[]> = {
        sprint: [],
        main: [fastestLapBonus],
        practice: [],
        qualifying: [],
        q1: [],
        q2: [],
        q3: [],
        timeTrial: [],
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'bestNResults',
        count: 6,
      };

      const championship: ChampionshipConfig = {
        id: 'driver-champ-sprint-main',
        name: 'Driver Championship',
        type: 'driver' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-sprint-main-driver`,
        seasonId,
        scoringPresetId: 'sprint-main-driver',
        championships: [championship],
      });
    },
  },
  {
    id: 'club-default',
    name: 'Club ladder',
    description:
      'Simple club ladder with a single main race and no bonuses or drop scores.',
    primaryChampionshipType: 'driver',
    dropPolicySummary: 'All race results count, no drop scores.',
    sessionSummary: 'Main race only',
    bonusSummary: 'No bonus points.',
    defaultTimings: {
      practiceMinutes: 20,
      qualifyingMinutes: 20,
      sprintRaceMinutes: 0,
      mainRaceMinutes: 40,
      sessionCount: 1,
    },
    createConfig: ({ seasonId }) => {
      const sessionTypes: SessionType[] = ['main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: new PointsTable({}),
        main: clubMainPoints,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'none',
      };

      const championship: ChampionshipConfig = {
        id: 'driver-champ-club-default',
        name: 'Driver Championship',
        type: 'driver' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-club-default`,
        seasonId,
        scoringPresetId: 'club-default',
        championships: [championship],
      });
    },
  },
  {
    id: 'endurance-main-double',
    name: 'Endurance weekend',
    description:
      'Single main endurance race with double points and a simple drop policy.',
    primaryChampionshipType: 'driver',
    dropPolicySummary: 'Best 4 results of 6 count towards the championship.',
    sessionSummary: 'Main race only',
    bonusSummary: 'No bonus points.',
    defaultTimings: {
      practiceMinutes: 30,
      qualifyingMinutes: 20,
      sprintRaceMinutes: 0,
      mainRaceMinutes: 120,
      sessionCount: 1,
    },
    createConfig: ({ seasonId }) => {
      const sessionTypes: SessionType[] = ['main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: new PointsTable({}),
        main: enduranceMainPoints,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'bestNResults',
        count: 4,
      };

      const championship: ChampionshipConfig = {
        id: 'driver-champ-endurance-main-double',
        name: 'Driver Championship',
        type: 'driver' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-endurance-main-double`,
        seasonId,
        scoringPresetId: 'endurance-main-double',
        championships: [championship],
      });
    },
  },
  {
    id: 'sprint-main-team',
    name: 'Sprint + Main (Teams)',
    description: 'Teams championship using the Sprint + Main weekend format.',
    primaryChampionshipType: 'team',
    dropPolicySummary: 'Best 6 results of 8 count towards the championship.',
    sessionSummary: 'Sprint + Main',
    bonusSummary: 'Fastest lap +1 point in main race if finishing P10 or better.',
    defaultTimings: {
      practiceMinutes: 20,
      qualifyingMinutes: 30,
      sprintRaceMinutes: 20,
      mainRaceMinutes: 40,
      sessionCount: 2,
    },
    createConfig: ({ seasonId }) => {
      const fastestLapBonus: BonusRule = {
        id: 'fastest-lap-main-team',
        type: 'fastestLap',
        points: 1,
        requiresFinishInTopN: 10,
      };

      const sessionTypes: SessionType[] = ['sprint', 'main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: sprintPointsSprintMain,
        main: mainPointsSprintMain,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const bonusRulesBySessionType: Record<SessionType, BonusRule[]> = {
        sprint: [],
        main: [fastestLapBonus],
        practice: [],
        qualifying: [],
        q1: [],
        q2: [],
        q3: [],
        timeTrial: [],
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'bestNResults',
        count: 6,
      };

      const championship: ChampionshipConfig = {
        id: 'team-champ-sprint-main',
        name: 'Team Championship',
        type: 'team' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        bonusRulesBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-sprint-main-team`,
        seasonId,
        scoringPresetId: 'sprint-main-team',
        championships: [championship],
      });
    },
  },
  {
    id: 'club-default-nations',
    name: 'Club ladder (Nations)',
    description: 'Nation vs nation ladder with a single main race and no bonuses.',
    primaryChampionshipType: 'nations',
    dropPolicySummary: 'All race results count, no drop scores.',
    sessionSummary: 'Main race only',
    bonusSummary: 'No bonus points.',
    defaultTimings: {
      practiceMinutes: 20,
      qualifyingMinutes: 20,
      sprintRaceMinutes: 0,
      mainRaceMinutes: 40,
      sessionCount: 1,
    },
    createConfig: ({ seasonId }) => {
      const sessionTypes: SessionType[] = ['main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: new PointsTable({}),
        main: clubMainPoints,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'none',
      };

      const championship: ChampionshipConfig = {
        id: 'nations-champ-club-default',
        name: 'Nations Championship',
        type: 'nations' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-club-default-nations`,
        seasonId,
        scoringPresetId: 'club-default-nations',
        championships: [championship],
      });
    },
  },
  {
    id: 'endurance-main-trophy',
    name: 'Endurance Trophy Event',
    description: 'Trophy-style endurance event with a single long main race.',
    primaryChampionshipType: 'trophy',
    dropPolicySummary: 'Best 4 results of 6 count towards the championship.',
    sessionSummary: 'Main race only',
    bonusSummary: 'No bonus points.',
    defaultTimings: {
      practiceMinutes: 30,
      qualifyingMinutes: 20,
      sprintRaceMinutes: 0,
      mainRaceMinutes: 120,
      sessionCount: 1,
    },
    createConfig: ({ seasonId }) => {
      const sessionTypes: SessionType[] = ['main'];

      const pointsTableBySessionType: Record<SessionType, PointsTable> = {
        sprint: new PointsTable({}),
        main: enduranceMainPoints,
        practice: new PointsTable({}),
        qualifying: new PointsTable({}),
        q1: new PointsTable({}),
        q2: new PointsTable({}),
        q3: new PointsTable({}),
        timeTrial: new PointsTable({}),
      };

      const dropScorePolicy: DropScorePolicy = {
        strategy: 'bestNResults',
        count: 4,
      };

      const championship: ChampionshipConfig = {
        id: 'trophy-champ-endurance-main',
        name: 'Trophy Championship',
        type: 'trophy' as ChampionshipType,
        sessionTypes,
        pointsTableBySessionType,
        dropScorePolicy,
      };

      return LeagueScoringConfig.create({
        id: `lsc-${seasonId}-endurance-main-trophy`,
        seasonId,
        scoringPresetId: 'endurance-main-trophy',
        championships: [championship],
      });
    },
  },
];

export function listLeagueScoringPresets(): LeagueScoringPreset[] {
  return [...leagueScoringPresets];
}

export function getLeagueScoringPresetById(
  id: string,
): LeagueScoringPreset | undefined {
  return leagueScoringPresets.find((preset) => preset.id === id);
}