import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import { Season } from '../../domain/entities/season/Season';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { Weekday } from '../../domain/types/Weekday';
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
import { SeasonDropPolicy } from '../../domain/value-objects/SeasonDropPolicy';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { SeasonScoringConfig } from '../../domain/value-objects/SeasonScoringConfig';
import { SeasonStewardingConfig } from '../../domain/value-objects/SeasonStewardingConfig';
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
/**
 * Input, result and error models shared across Season-focused use cases.
 */

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 interface 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;
}

// Backwards-compatible alias for existing wiring/tests.
export type CreateSeasonForLeagueCommand = CreateSeasonForLeagueInput;

export type CreateSeasonForLeagueResult = {
  season: Season;
};

export type CreateSeasonForLeagueErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'SOURCE_SEASON_NOT_FOUND'
  | 'REPOSITORY_ERROR';

export type CreateSeasonForLeagueApplicationError = ApplicationErrorCode<
  CreateSeasonForLeagueErrorCode,
  { message: string }
>;

export interface ListSeasonsForLeagueInput {
  leagueId: string;
}

// Backwards-compatible alias for existing wiring/tests.
export type ListSeasonsForLeagueQuery = ListSeasonsForLeagueInput;

export interface ListSeasonsForLeagueResult {
  seasons: Season[];
}

export type ListSeasonsForLeagueErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'REPOSITORY_ERROR';

export type ListSeasonsForLeagueApplicationError = ApplicationErrorCode<
  ListSeasonsForLeagueErrorCode,
  { message: string }
>;

export interface GetSeasonDetailsInput {
  leagueId: string;
  seasonId: string;
}

// Backwards-compatible alias for existing wiring/tests.
export type GetSeasonDetailsQuery = GetSeasonDetailsInput;

export interface GetSeasonDetailsResult {
  season: Season;
}

export type GetSeasonDetailsErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'SEASON_NOT_FOUND'
  | 'REPOSITORY_ERROR';

export type GetSeasonDetailsApplicationError = ApplicationErrorCode<
  GetSeasonDetailsErrorCode,
  { message: string }
>;

export type SeasonLifecycleTransition =
  | 'activate'
  | 'complete'
  | 'archive'
  | 'cancel';

export interface ManageSeasonLifecycleInput {
  leagueId: string;
  seasonId: string;
  transition: SeasonLifecycleTransition;
}

// Backwards-compatible alias for existing wiring/tests.
export type ManageSeasonLifecycleCommand = ManageSeasonLifecycleInput;

export interface ManageSeasonLifecycleResult {
  season: Season;
}

export type ManageSeasonLifecycleErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'SEASON_NOT_FOUND'
  | 'INVALID_LIFECYCLE_TRANSITION'
  | 'REPOSITORY_ERROR';

export type ManageSeasonLifecycleApplicationError = ApplicationErrorCode<
  ManageSeasonLifecycleErrorCode,
  { message: string }
>;

/**
 * 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: LeagueRepository,
    private readonly seasonRepository: SeasonRepository) {}

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

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

      if (command.sourceSeasonId) {
        const source = await this.seasonRepository.findById(command.sourceSeasonId);
        if (!source) {
          return Result.err({
            code: 'SOURCE_SEASON_NOT_FOUND',
            details: {
              message: `Source Season not found: ${command.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 (command.config) {
        baseSeasonProps = this.deriveSeasonPropsFromConfig(command.config);
      }

      const seasonId = uuidv4();

      const season = Season.create({
        id: seasonId,
        leagueId: league.id.toString(),
        gameId: command.gameId,
        name: command.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);

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

  private deriveSeasonPropsFromConfig(config: LeagueConfigFormModel): {
    schedule?: SeasonSchedule;
    scoringConfig?: SeasonScoringConfig;
    dropPolicy?: SeasonDropPolicy;
    stewardingConfig?: SeasonStewardingConfig;
    maxDrivers?: number;
  } {
    const schedule = this.buildScheduleFromTimings(config);

    const scoring = config.scoring ?? {};
    const scoringConfig = new SeasonScoringConfig({
      scoringPresetId: scoring.patternId ?? 'custom',
      customScoringEnabled: scoring.customScoringEnabled ?? false,
    });

    const dropPolicyInput = config.dropPolicy ?? {};
    const dropStrategy = dropPolicyInput.strategy;
    const dropPolicy = new SeasonDropPolicy({
      strategy:
        dropStrategy === 'none' ||
        dropStrategy === 'bestNResults' ||
        dropStrategy === 'dropWorstN'
          ? dropStrategy
          : 'none',
      ...(dropPolicyInput.n !== undefined ? { n: dropPolicyInput.n } : {}),
    });

    const stewardingInput = config.stewarding ?? {};
    const decisionMode = stewardingInput.decisionMode;
    const stewardingConfig = new SeasonStewardingConfig({
      decisionMode:
        decisionMode === 'admin_only' ||
        decisionMode === 'steward_decides' ||
        decisionMode === 'steward_vote' ||
        decisionMode === 'member_vote' ||
        decisionMode === 'steward_veto' ||
        decisionMode === 'member_veto'
          ? decisionMode
          : 'admin_only',
      ...(stewardingInput.requiredVotes !== undefined
        ? { requiredVotes: stewardingInput.requiredVotes }
        : {}),
      requireDefense: stewardingInput.requireDefense ?? false,
      defenseTimeLimit: stewardingInput.defenseTimeLimit ?? 48,
      voteTimeLimit: stewardingInput.voteTimeLimit ?? 72,
      protestDeadlineHours: stewardingInput.protestDeadlineHours ?? 48,
      stewardingClosesHours: stewardingInput.stewardingClosesHours ?? 168,
      notifyAccusedOnProtest: stewardingInput.notifyAccusedOnProtest ?? true,
      notifyOnVoteRequired: stewardingInput.notifyOnVoteRequired ?? true,
    });

    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.timings;
    if (!timings?.seasonStartDate || !timings.raceStartTime) {
      return undefined;
    }

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

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

    if (!Number.isInteger(plannedRounds) || plannedRounds <= 0) {
      return undefined;
    }

    const weekdaysRaw = timings.weekdays ?? [];
    const weekdays = WeekdaySet.fromArray(
      weekdaysRaw
        .filter((d): d is Weekday => (['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as const).includes(d as Weekday))
        .slice(0),
    );

    const safeWeekdays = weekdays.getAll().length > 0 ? weekdays : WeekdaySet.fromArray(['Mon']);

    const recurrence = (() => {
      switch (timings.recurrenceStrategy) {
        case 'everyNWeeks':
          return RecurrenceStrategyFactory.everyNWeeks(
            timings.intervalWeeks ?? 2,
            safeWeekdays,
          );
        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(safeWeekdays);
      }
    })();

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

/**
 * ListSeasonsForLeagueUseCase
 */
export class ListSeasonsForLeagueUseCase {
  constructor(private readonly leagueRepository: LeagueRepository,
    private readonly seasonRepository: SeasonRepository) {}

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

      const seasons = await this.seasonRepository.listByLeague(league.id.toString());

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

/**
 * GetSeasonDetailsUseCase
 */
export class GetSeasonDetailsUseCase {
  constructor(private readonly leagueRepository: LeagueRepository,
    private readonly seasonRepository: SeasonRepository) {}

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

      const season = await this.seasonRepository.findById(query.seasonId);
      if (!season || season.leagueId !== league.id.toString()) {
        return Result.err({
          code: 'SEASON_NOT_FOUND',
          details: {
            message: `Season ${query.seasonId} does not belong to league ${league.id}`,
          },
        });
      }

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

/**
 * ManageSeasonLifecycleUseCase
 */
export class ManageSeasonLifecycleUseCase {
  constructor(private readonly leagueRepository: LeagueRepository,
    private readonly seasonRepository: SeasonRepository) {}

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

      const season = await this.seasonRepository.findById(command.seasonId);
      if (!season || season.leagueId !== league.id.toString()) {
        return Result.err({
          code: 'SEASON_NOT_FOUND',
          details: {
            message: `Season ${command.seasonId} does not belong to league ${league.id}`,
          },
        });
      }

      let updated: Season;
      try {
        switch (command.transition) {
          case 'activate':
            updated = season.activate();
            break;
          case 'complete':
            updated = season.complete();
            break;
          case 'archive':
            updated = season.archive();
            break;
          case 'cancel':
            updated = season.cancel();
            break;
          default:
            return Result.err({
              code: 'INVALID_LIFECYCLE_TRANSITION',
              details: {
                message: `Unsupported Season lifecycle transition: ${command.transition}`,
              },
            });
        }
      } catch (error) {
        return Result.err({
          code: 'INVALID_LIFECYCLE_TRANSITION',
          details: {
            message:
              error instanceof Error ? error.message : 'Invalid lifecycle transition',
          },
        });
      }

      try {
        await this.seasonRepository.update(updated);
      } catch (error) {
        return Result.err({
          code: 'REPOSITORY_ERROR',
          details: {
            message: error instanceof Error ? error.message : 'Unknown error',
          },
        });
      }

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