import type { LeagueTimingsFormDTO } from './LeagueConfigFormDTO';
import type { Weekday } from '../../domain/types/Weekday';
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
import type { RecurrenceStrategy } from '../../domain/value-objects/RecurrenceStrategy';
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { BusinessRuleViolationError } from '../errors/RacingApplicationError';

export interface LeagueScheduleDTO {
  seasonStartDate: string;
  raceStartTime: string;
  timezoneId: string;
  recurrenceStrategy: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
  intervalWeeks?: number | undefined;
  weekdays?: Weekday[] | undefined;
  monthlyOrdinal?: 1 | 2 | 3 | 4 | undefined;
  monthlyWeekday?: Weekday | undefined;
  plannedRounds: number;
}

export interface LeagueSchedulePreviewDTO {
  rounds: Array<{ roundNumber: number; scheduledAt: string; timezoneId: string }>;
  summary: string;
}

export function leagueTimingsToScheduleDTO(
  timings: LeagueTimingsFormDTO,
): LeagueScheduleDTO | null {
  if (
    !timings.seasonStartDate ||
    !timings.raceStartTime ||
    !timings.timezoneId ||
    !timings.recurrenceStrategy ||
    !timings.roundsPlanned
  ) {
    return null;
  }

  return {
    seasonStartDate: timings.seasonStartDate,
    raceStartTime: timings.raceStartTime,
    timezoneId: timings.timezoneId,
    recurrenceStrategy: timings.recurrenceStrategy,
    intervalWeeks: timings.intervalWeeks,
    weekdays: timings.weekdays,
    monthlyOrdinal: timings.monthlyOrdinal,
    monthlyWeekday: timings.monthlyWeekday,
    plannedRounds: timings.roundsPlanned,
  };
}

export function scheduleDTOToSeasonSchedule(dto: LeagueScheduleDTO): SeasonSchedule {
  if (!dto.seasonStartDate) {
    throw new BusinessRuleViolationError('seasonStartDate is required');
  }
  if (!dto.raceStartTime) {
    throw new BusinessRuleViolationError('raceStartTime is required');
  }
  if (!dto.timezoneId) {
    throw new BusinessRuleViolationError('timezoneId is required');
  }
  if (!dto.recurrenceStrategy) {
    throw new BusinessRuleViolationError('recurrenceStrategy is required');
  }
  if (!Number.isInteger(dto.plannedRounds) || dto.plannedRounds <= 0) {
    throw new BusinessRuleViolationError('plannedRounds must be a positive integer');
  }

  const startDate = new Date(dto.seasonStartDate);
  if (Number.isNaN(startDate.getTime())) {
    throw new BusinessRuleViolationError(
      `seasonStartDate must be a valid date, got "${dto.seasonStartDate}"`,
    );
  }

  const timeOfDay = RaceTimeOfDay.fromString(dto.raceStartTime);
  const timezone = new LeagueTimezone(dto.timezoneId);

  let recurrence: RecurrenceStrategy;

  if (dto.recurrenceStrategy === 'weekly') {
    if (!dto.weekdays || dto.weekdays.length === 0) {
      throw new BusinessRuleViolationError('weekdays are required for weekly recurrence');
    }
    recurrence = RecurrenceStrategyFactory.weekly(new WeekdaySet(dto.weekdays));
  } else if (dto.recurrenceStrategy === 'everyNWeeks') {
    if (!dto.weekdays || dto.weekdays.length === 0) {
      throw new BusinessRuleViolationError('weekdays are required for everyNWeeks recurrence');
    }
    if (dto.intervalWeeks == null) {
      throw new BusinessRuleViolationError(
        'intervalWeeks is required for everyNWeeks recurrence',
      );
    }
    recurrence = RecurrenceStrategyFactory.everyNWeeks(
      dto.intervalWeeks,
      new WeekdaySet(dto.weekdays),
    );
  } else if (dto.recurrenceStrategy === 'monthlyNthWeekday') {
    if (!dto.monthlyOrdinal || !dto.monthlyWeekday) {
      throw new BusinessRuleViolationError(
        'monthlyOrdinal and monthlyWeekday are required for monthlyNthWeekday',
      );
    }
    const pattern = new MonthlyRecurrencePattern(dto.monthlyOrdinal, dto.monthlyWeekday);
    recurrence = RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
  } else {
    throw new BusinessRuleViolationError(`Unknown recurrenceStrategy "${dto.recurrenceStrategy}"`);
  }

  return new SeasonSchedule({
    startDate,
    timeOfDay,
    timezone,
    recurrence,
    plannedRounds: dto.plannedRounds,
  });
}