import { Season } from '../../domain/entities/season/Season';
import { League } from '../../domain/entities/League';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { SeasonScoringConfig } from '../../domain/value-objects/SeasonScoringConfig';
import { SeasonDropPolicy, type SeasonDropStrategy } from '../../domain/value-objects/SeasonDropPolicy';
import { SeasonStewardingConfig } from '../../domain/value-objects/SeasonStewardingConfig';
import type { StewardingDecisionMode } from '../../domain/entities/League';
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
import type { Weekday } from '../../domain/types/Weekday';
import { v4 as uuidv4 } from 'uuid';
import type { UseCaseOutputPort } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

export type LeagueConfigFormModel = {
  basics?: {
    name?: string;
    description?: string;
    visibility?: string;
    gameId?: string;
  };
  structure?: {
    mode?: string;
    maxDrivers?: number;
  };
  championships?: {
    enableDriverChampionship?: boolean;
    enableTeamChampionship?: boolean;
    enableNationsChampionship?: boolean;
    enableTrophyChampionship?: boolean;
  };
  scoring?: {
    patternId?: string;
    customScoringEnabled?: boolean;
  };
  dropPolicy?: {
    strategy?: string;
    n?: number;
  };
  timings?: {
    qualifyingMinutes?: number;
    mainRaceMinutes?: number;
    sessionCount?: number;
    roundsPlanned?: number;
    seasonStartDate?: string;
    raceStartTime?: string;
    timezoneId?: string;
    recurrenceStrategy?: string;
    weekdays?: string[];
    intervalWeeks?: number;
    monthlyOrdinal?: number;
    monthlyWeekday?: string;
  };
  stewarding?: {
    decisionMode?: string;
    requiredVotes?: number;
    requireDefense?: boolean;
    defenseTimeLimit?: number;
    voteTimeLimit?: number;
    protestDeadlineHours?: number;
    stewardingClosesHours?: number;
    notifyAccusedOnProtest?: boolean;
    notifyOnVoteRequired?: boolean;
  };
};

export type CreateSeasonForLeagueInput = {
  leagueId: string;
  name: string;
  gameId: string;
  sourceSeasonId?: string;
  /**
   * Optional high-level wizard config used to derive schedule/scoring/drop/stewarding.
   * When omitted, the Season will be created with minimal metadata only.
   */
  config?: LeagueConfigFormModel;
};

export type CreateSeasonForLeagueResult = {
  league: League;
  season: Season;
};

type CreateSeasonForLeagueErrorCode = 'LEAGUE_NOT_FOUND' | 'VALIDATION_ERROR' | 'REPOSITORY_ERROR';

/**
 * CreateSeasonForLeagueUseCase
 *
 * Creates a new Season for an existing League, optionally cloning or deriving
 * configuration from a source Season or a league config form.
 */
export class CreateSeasonForLeagueUseCase {
  constructor(
    private readonly leagueRepository: ILeagueRepository,
    private readonly seasonRepository: ISeasonRepository,
    private readonly output: UseCaseOutputPort<CreateSeasonForLeagueResult>,
  ) {}

  async execute(
    input: CreateSeasonForLeagueInput,
  ): Promise<Result<void, ApplicationErrorCode<CreateSeasonForLeagueErrorCode>>> {
    try {
      const league = await this.leagueRepository.findById(input.leagueId);
      if (!league) {
        return Result.err({
          code: 'LEAGUE_NOT_FOUND',
          details: { message: `League not found: ${input.leagueId}` },
        });
      }

      let baseSeasonProps: {
        schedule?: SeasonSchedule;
        scoringConfig?: SeasonScoringConfig;
        dropPolicy?: SeasonDropPolicy;
        stewardingConfig?: SeasonStewardingConfig;
        maxDrivers?: number;
      } = {};

      if (input.sourceSeasonId) {
        const source = await this.seasonRepository.findById(input.sourceSeasonId);
        if (!source) {
          return Result.err({
            code: 'VALIDATION_ERROR',
            details: { message: `Source Season not found: ${input.sourceSeasonId}` },
          });
        }
        baseSeasonProps = {
          ...(source.schedule !== undefined ? { schedule: source.schedule } : {}),
          ...(source.scoringConfig !== undefined ? { scoringConfig: source.scoringConfig } : {}),
          ...(source.dropPolicy !== undefined ? { dropPolicy: source.dropPolicy } : {}),
          ...(source.stewardingConfig !== undefined ? { stewardingConfig: source.stewardingConfig } : {}),
          ...(source.maxDrivers !== undefined ? { maxDrivers: source.maxDrivers } : {}),
        };
      } else if (input.config) {
        baseSeasonProps = this.deriveSeasonPropsFromConfig(input.config);
      }

      const seasonId = uuidv4();

      const season = Season.create({
        id: seasonId,
        leagueId: league.id.toString(),
        gameId: input.gameId,
        name: input.name,
        year: new Date().getFullYear(),
        status: 'planned',
        ...(baseSeasonProps?.schedule ? { schedule: baseSeasonProps.schedule } : {}),
        ...(baseSeasonProps?.scoringConfig ? { scoringConfig: baseSeasonProps.scoringConfig } : {}),
        ...(baseSeasonProps?.dropPolicy ? { dropPolicy: baseSeasonProps.dropPolicy } : {}),
        ...(baseSeasonProps?.stewardingConfig ? { stewardingConfig: baseSeasonProps.stewardingConfig } : {}),
        ...(baseSeasonProps?.maxDrivers !== undefined ? { maxDrivers: baseSeasonProps.maxDrivers } : {}),
      });

      await this.seasonRepository.add(season);

      this.output.present({ league, season });

      return Result.ok(undefined);
    } catch (error) {
      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: {
          message: error instanceof Error ? error.message : 'Unknown error',
        },
      });
    }
  }

  private parseDropStrategy(value: unknown): SeasonDropStrategy {
    if (value === 'none' || value === 'bestNResults' || value === 'dropWorstN') {
      return value;
    }
    return 'none';
  }

  private parseDecisionMode(value: unknown): StewardingDecisionMode {
    if (
      value === 'admin_only' ||
      value === 'steward_decides' ||
      value === 'steward_vote' ||
      value === 'member_vote' ||
      value === 'steward_veto' ||
      value === 'member_veto'
    ) {
      return value;
    }
    return 'admin_only';
  }

  private deriveSeasonPropsFromConfig(config: LeagueConfigFormModel): {
    schedule?: SeasonSchedule;
    scoringConfig?: SeasonScoringConfig;
    dropPolicy?: SeasonDropPolicy;
    stewardingConfig?: SeasonStewardingConfig;
    maxDrivers?: number;
  } {
    const schedule = this.buildScheduleFromTimings(config);
    const scoringConfig = new SeasonScoringConfig({
      scoringPresetId: config.scoring?.patternId ?? 'custom',
      customScoringEnabled: config.scoring?.customScoringEnabled ?? false,
    });
    const dropPolicy = new SeasonDropPolicy({
      strategy: this.parseDropStrategy(config.dropPolicy?.strategy),
      ...(config.dropPolicy?.n !== undefined ? { n: config.dropPolicy.n } : {}),
    });
    const stewardingConfig = new SeasonStewardingConfig({
      decisionMode: this.parseDecisionMode(config.stewarding?.decisionMode),
      ...(config.stewarding?.requiredVotes !== undefined
        ? { requiredVotes: config.stewarding.requiredVotes }
        : {}),
      requireDefense: config.stewarding?.requireDefense ?? false,
      defenseTimeLimit: config.stewarding?.defenseTimeLimit ?? 0,
      voteTimeLimit: config.stewarding?.voteTimeLimit ?? 0,
      protestDeadlineHours: config.stewarding?.protestDeadlineHours ?? 0,
      stewardingClosesHours: config.stewarding?.stewardingClosesHours ?? 0,
      notifyAccusedOnProtest: config.stewarding?.notifyAccusedOnProtest ?? false,
      notifyOnVoteRequired: config.stewarding?.notifyOnVoteRequired ?? false,
    });

    const structure = config.structure ?? {};
    const maxDrivers =
      typeof structure.maxDrivers === 'number' && structure.maxDrivers > 0
        ? structure.maxDrivers
        : undefined;

    return {
      ...(schedule !== undefined ? { schedule } : {}),
      scoringConfig,
      dropPolicy,
      stewardingConfig,
      ...(maxDrivers !== undefined ? { maxDrivers } : {}),
    };
  }

  private buildScheduleFromTimings(
    config: LeagueConfigFormModel,
  ): SeasonSchedule | undefined {
    const { timings } = config;
    if (!timings || !timings.seasonStartDate || !timings.raceStartTime) {
      return undefined;
    }

    const startDate = new Date(timings.seasonStartDate);
    const timeOfDay = RaceTimeOfDay.fromString(timings.raceStartTime);
    const timezoneId = timings.timezoneId ?? 'UTC';
    const timezone = LeagueTimezone.create(timezoneId);

    const plannedRounds =
      typeof timings.roundsPlanned === 'number' && timings.roundsPlanned > 0
        ? timings.roundsPlanned
        : timings.sessionCount;

    const recurrence = (() => {
      const weekdays: WeekdaySet =
        timings.weekdays && timings.weekdays.length > 0
          ? WeekdaySet.fromArray(
              timings.weekdays as unknown as Weekday[],
            )
          : WeekdaySet.fromArray(['Mon']);
      switch (timings.recurrenceStrategy) {
        case 'everyNWeeks':
          return RecurrenceStrategyFactory.everyNWeeks(
            timings.intervalWeeks ?? 2,
            weekdays,
          );
        case 'monthlyNthWeekday': {
          const pattern = MonthlyRecurrencePattern.create(
            (timings.monthlyOrdinal ?? 1) as 1 | 2 | 3 | 4,
            (timings.monthlyWeekday ?? 'Mon') as Weekday,
          );
          return RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
        }
        case 'weekly':
        default:
          return RecurrenceStrategyFactory.weekly(weekdays);
      }
    })();

    return new SeasonSchedule({
      startDate,
      timeOfDay,
      timezone,
      recurrence,
      plannedRounds: plannedRounds ?? 0,
    });
  }
}
