/**
 * Application Use Case: ApplyPenaltyUseCase
 *
 * Allows a steward to apply a penalty to a driver for an incident during a race.
 * The penalty can be standalone or linked to an upheld protest.
 */

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { randomUUID } from 'crypto';
import { Penalty } from '../../domain/entities/penalty/Penalty';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';

export interface ApplyPenaltyInput {
  raceId: string;
  driverId: string;
  stewardId: string;
  type: Penalty['type'];
  value?: Penalty['value'];
  reason: string;
  protestId?: string;
  notes?: string;
}

export interface ApplyPenaltyResult {
  penaltyId: string;
}

export class ApplyPenaltyUseCase {
  constructor(
    private readonly penaltyRepository: PenaltyRepository,
    private readonly protestRepository: ProtestRepository,
    private readonly raceRepository: RaceRepository,
    private readonly leagueMembershipRepository: LeagueMembershipRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    command: ApplyPenaltyInput,
  ): Promise<
    Result<
      ApplyPenaltyResult,
      ApplicationErrorCode<
        | 'RACE_NOT_FOUND'
        | 'INSUFFICIENT_AUTHORITY'
        | 'PROTEST_NOT_FOUND'
        | 'PROTEST_NOT_UPHELD'
        | 'PROTEST_NOT_FOR_RACE'
      >
    >
  > {
    this.logger.debug('ApplyPenaltyUseCase: Executing with command', command);

    // Validate race exists
    const race = await this.raceRepository.findById(command.raceId);
    if (!race) {
      this.logger.warn(`ApplyPenaltyUseCase: Race with ID ${command.raceId} not found.`);
      return Result.err({ code: 'RACE_NOT_FOUND' });
    }
    this.logger.debug(`ApplyPenaltyUseCase: Race ${race.id} found.`);

    // Validate steward has authority (owner or admin of the league)
    const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
    const stewardMembership = memberships.find(
      (m) => m.driverId.toString() === command.stewardId && m.status.toString() === 'active'
    );

    if (!stewardMembership || (stewardMembership.role.toString() !== 'owner' && stewardMembership.role.toString() !== 'admin')) {
      this.logger.warn(`ApplyPenaltyUseCase: Steward ${command.stewardId} does not have authority for league ${race.leagueId}.`);
      return Result.err({ code: 'INSUFFICIENT_AUTHORITY' });
    }
    this.logger.debug(`ApplyPenaltyUseCase: Steward ${command.stewardId} has authority.`);

    // If linked to a protest, validate the protest exists and is upheld
    if (command.protestId) {
      const protest = await this.protestRepository.findById(command.protestId);
      if (!protest) {
        this.logger.warn(`ApplyPenaltyUseCase: Protest with ID ${command.protestId} not found.`);
        return Result.err({ code: 'PROTEST_NOT_FOUND' });
      }
      if (protest.status.toString() !== 'upheld') {
        this.logger.warn(`ApplyPenaltyUseCase: Protest ${protest.id} is not upheld. Status: ${protest.status}`);
        return Result.err({ code: 'PROTEST_NOT_UPHELD' });
      }
      if (protest.raceId !== command.raceId) {
        this.logger.warn(`ApplyPenaltyUseCase: Protest ${protest.id} is for race ${protest.raceId}, not ${command.raceId}.`);
        return Result.err({ code: 'PROTEST_NOT_FOR_RACE' });
      }
      this.logger.debug(`ApplyPenaltyUseCase: Protest ${protest.id} is valid and upheld.`);
    }

    // Create the penalty
    const penalty = Penalty.create({
      id: randomUUID(),
      leagueId: race.leagueId,
      raceId: command.raceId,
      driverId: command.driverId,
      type: command.type,
      ...(command.value !== undefined ? { value: command.value } : {}),
      reason: command.reason,
      ...(command.protestId !== undefined ? { protestId: command.protestId } : {}),
      issuedBy: command.stewardId,
      status: 'pending',
      issuedAt: new Date(),
      ...(command.notes !== undefined ? { notes: command.notes } : {}),
    });

    await this.penaltyRepository.create(penalty);
    this.logger.info(
      `ApplyPenaltyUseCase: Successfully applied penalty ${penalty.id} for driver ${command.driverId} in race ${command.raceId}.`,
    );

    const result: ApplyPenaltyResult = { penaltyId: penalty.id.toString() };

    return Result.ok(result);
  }
}