import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { NotificationService } from '../../../notifications/application/ports/NotificationService';
import type { NotificationType } from '../../../notifications/domain/types/NotificationTypes';
import type { RaceEvent } from '../../domain/entities/RaceEvent';
import { IncidentCount } from '../../domain/entities/result/IncidentCount';
import { Position } from '../../domain/entities/result/Position';
import type { Result as RaceResult } from '../../domain/entities/result/Result';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { isLeagueStewardOrHigherRole } from '../../domain/types/LeagueRoles';

export type SendPerformanceSummaryInput = {
  leagueId: string;
  raceId: string;
  driverId: string;
  triggeredById: string;
};

export type SendPerformanceSummaryResult = {
  leagueId: string;
  raceId: string;
  driverId: string;
  notificationsSent: number;
};

export type SendPerformanceSummaryErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'RACE_NOT_FOUND'
  | 'DRIVER_NOT_FOUND'
  | 'INSUFFICIENT_PERMISSIONS'
  | 'SUMMARY_NOT_AVAILABLE'
  | 'REPOSITORY_ERROR';

/**
 * Use Case: SendPerformanceSummaryUseCase
 *
 * Sends an immediate performance summary notification to a driver
 * for a specific race event.
 */
export class SendPerformanceSummaryUseCase {
  constructor(private readonly notificationService: NotificationService,
    private readonly raceEventRepository: RaceEventRepository,
    private readonly resultRepository: ResultRepository,
    private readonly leagueRepository: LeagueRepository,
    private readonly membershipRepository: LeagueMembershipRepository,
    private readonly driverRepository: DriverRepository,
    private readonly logger: Logger) {}

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

      const raceEvent = await this.raceEventRepository.findById(input.raceId);
      if (!raceEvent) {
        return Result.err({ code: 'RACE_NOT_FOUND', details: { message: 'Race event not found' } });
      }

      const driver = await this.driverRepository.findById(input.driverId);
      if (!driver) {
        return Result.err({ code: 'DRIVER_NOT_FOUND', details: { message: 'Driver not found' } });
      }

      if (input.triggeredById !== input.driverId) {
        const membership = await this.membershipRepository.getMembership(
          league.id.toString(),
          input.triggeredById,
        );
        if (!membership || !isLeagueStewardOrHigherRole(membership.role.toString())) {
          return Result.err({
            code: 'INSUFFICIENT_PERMISSIONS',
            details: { message: 'Insufficient permissions to send performance summary' },
          });
        }
      }

      const mainRaceSession = raceEvent.getMainRaceSession();
      if (!mainRaceSession || mainRaceSession.status !== 'completed') {
        return Result.err({
          code: 'SUMMARY_NOT_AVAILABLE',
          details: { message: 'Performance summary is not available for this race' },
        });
      }

      const results = await this.resultRepository.findByRaceId(mainRaceSession.id);
      const driverResult = results.find((r: RaceResult) => r.driverId.toString() === input.driverId);

      if (!driverResult) {
        return Result.err({
          code: 'SUMMARY_NOT_AVAILABLE',
          details: { message: 'Performance summary is not available for this driver' },
        });
      }

      let notificationsSent = 0;

      await this.sendPerformanceSummaryNotification(input.driverId, raceEvent, driverResult, league.id.toString());
      notificationsSent += 1;

      const result: SendPerformanceSummaryResult = {
        leagueId: league.id.toString(),
        raceId: raceEvent.id,
        driverId: input.driverId,
        notificationsSent,
      };

      return Result.ok(result);
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : 'Failed to send performance summary';
      this.logger.error('SendPerformanceSummaryUseCase.execute failed', error instanceof Error ? error : undefined);
      return Result.err({ code: 'REPOSITORY_ERROR', details: { message } });
    }
  }

  private async sendPerformanceSummaryNotification(
    driverId: string,
    raceEvent: RaceEvent,
    driverResult: RaceResult | undefined,
    leagueId: string,
  ): Promise<void> {
    const position = driverResult?.position ?? 'DNF';
    const positionChange = driverResult?.getPositionChange() ?? 0;
    const incidents = driverResult?.incidents ?? 0;

    const provisionalRatingChange = this.calculateProvisionalRatingChange(driverResult?.position?.toNumber(), driverResult?.incidents?.toNumber());

    const title = `Race Complete: ${raceEvent.name}`;
    const positionValue = position instanceof Position ? position.toNumber() : position;
    const incidentValue = incidents instanceof IncidentCount ? incidents.toNumber() : incidents;
    const body = this.buildPerformanceSummaryBody(positionValue, positionChange, incidentValue, provisionalRatingChange);

    await this.notificationService.sendNotification({
      recipientId: driverId,
      type: 'race_performance_summary' as NotificationType,
      title,
      body,
      channel: 'in_app',
      urgency: 'modal',
      data: {
        raceEventId: raceEvent.id,
        sessionId: raceEvent.getMainRaceSession()?.id ?? '',
        leagueId,
        position: positionValue,
        positionChange,
        incidents: incidentValue,
        provisionalRatingChange,
      },
      actions: [
        {
          label: 'View Full Results',
          type: 'primary',
          href: `/leagues/${leagueId}/races/${raceEvent.id}`,
        },
      ],
      requiresResponse: false,
    });
  }

  private buildPerformanceSummaryBody(
    position: number | 'DNF',
    positionChange: number,
    incidents: number,
    provisionalRatingChange: number,
  ): string {
    const positionText = position === 'DNF' ? 'DNF' : `P${position}`;
    const positionChangeText = positionChange > 0 ? `+${positionChange}` : positionChange < 0 ? `${positionChange}` : '±0';
    const incidentsText = incidents === 0 ? 'Clean race!' : `${incidents} incident${incidents > 1 ? 's' : ''}`;
    const ratingText = provisionalRatingChange >= 0 ? `+${provisionalRatingChange} rating` : `${provisionalRatingChange} rating`;

    return `You finished ${positionText} (${positionChangeText} positions). ${incidentsText} Provisional ${ratingText}.`;
  }

  private calculateProvisionalRatingChange(position?: number, incidents?: number): number {
    if (!position) return -10;

    const baseChange = position <= 3 ? 25 : position <= 10 ? 10 : -5;
    const positionBonus = Math.max(0, (20 - position) * 2);
    const incidentPenalty = (incidents ?? 0) * -5;

    return baseChange + positionBonus + incidentPenalty;
  }
}
