import { TeamRatingEvent } from '../entities/TeamRatingEvent';
import { TeamRatingEventId } from '../value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '../value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '../value-objects/TeamRatingDelta';
import { TeamDrivingReasonCode } from '../value-objects/TeamDrivingReasonCode';
import { TeamDrivingRatingCalculator, TeamDrivingRaceResult, TeamDrivingQualifyingResult, TeamDrivingOvertakeStats } from './TeamDrivingRatingCalculator';

export interface TeamDrivingRaceFactsDto {
  raceId: string;
  teamId: string;
  results: Array<{
    teamId: string;
    position: number;
    incidents: number;
    status: 'finished' | 'dnf' | 'dsq' | 'dns' | 'afk';
    fieldSize: number;
    strengthOfField: number;
    pace?: number;
    consistency?: number;
    teamwork?: number;
    sportsmanship?: number;
  }>;
}

export interface TeamDrivingQualifyingFactsDto {
  raceId: string;
  results: Array<{
    teamId: string;
    qualifyingPosition: number;
    fieldSize: number;
  }>;
}

export interface TeamDrivingOvertakeFactsDto {
  raceId: string;
  results: Array<{
    teamId: string;
    overtakes: number;
    successfulDefenses: number;
  }>;
}

/**
 * Domain Service: TeamDrivingRatingEventFactory
 *
 * Factory for creating team driving rating events using the full TeamDrivingRatingCalculator.
 * Mirrors user slice 3 pattern in core/racing/.
 * 
 * Pure domain logic - no persistence concerns.
 */
export class TeamDrivingRatingEventFactory {
  /**
   * Create rating events from a team's race finish.
   * Uses TeamDrivingRatingCalculator for comprehensive calculations.
   */
  static createFromRaceFinish(input: {
    teamId: string;
    position: number;
    incidents: number;
    status: 'finished' | 'dnf' | 'dsq' | 'dns' | 'afk';
    fieldSize: number;
    strengthOfField: number;
    raceId: string;
    pace?: number;
    consistency?: number;
    teamwork?: number;
    sportsmanship?: number;
  }): TeamRatingEvent[] {
    const result: TeamDrivingRaceResult = {
      teamId: input.teamId,
      position: input.position,
      incidents: input.incidents,
      status: input.status,
      fieldSize: input.fieldSize,
      strengthOfField: input.strengthOfField,
      raceId: input.raceId,
      pace: input.pace as number | undefined,
      consistency: input.consistency as number | undefined,
      teamwork: input.teamwork as number | undefined,
      sportsmanship: input.sportsmanship as number | undefined,
    };

    return TeamDrivingRatingCalculator.calculateFromRaceFinish(result);
  }

  /**
   * Create rating events from multiple race results.
   * Returns events grouped by team ID.
   */
  static createDrivingEventsFromRace(raceFacts: TeamDrivingRaceFactsDto): Map<string, TeamRatingEvent[]> {
    const eventsByTeam = new Map<string, TeamRatingEvent[]>();

    for (const result of raceFacts.results) {
      const input: {
        teamId: string;
        position: number;
        incidents: number;
        status: 'finished' | 'dnf' | 'dsq' | 'dns' | 'afk';
        fieldSize: number;
        strengthOfField: number;
        raceId: string;
        pace?: number;
        consistency?: number;
        teamwork?: number;
        sportsmanship?: number;
      } = {
        teamId: result.teamId,
        position: result.position,
        incidents: result.incidents,
        status: result.status,
        fieldSize: raceFacts.results.length,
        strengthOfField: result.strengthOfField,
        raceId: raceFacts.raceId,
      };

      if (result.pace !== undefined) {
        input.pace = result.pace;
      }
      if (result.consistency !== undefined) {
        input.consistency = result.consistency;
      }
      if (result.teamwork !== undefined) {
        input.teamwork = result.teamwork;
      }
      if (result.sportsmanship !== undefined) {
        input.sportsmanship = result.sportsmanship;
      }

      const events = this.createFromRaceFinish(input);

      if (events.length > 0) {
        eventsByTeam.set(result.teamId, events);
      }
    }

    return eventsByTeam;
  }

  /**
   * Create rating events from qualifying results.
   * Uses TeamDrivingRatingCalculator for qualifying calculations.
   */
  static createFromQualifying(input: {
    teamId: string;
    qualifyingPosition: number;
    fieldSize: number;
    raceId: string;
  }): TeamRatingEvent[] {
    const result: TeamDrivingQualifyingResult = {
      teamId: input.teamId,
      qualifyingPosition: input.qualifyingPosition,
      fieldSize: input.fieldSize,
      raceId: input.raceId,
    };

    return TeamDrivingRatingCalculator.calculateFromQualifying(result);
  }

  /**
   * Create rating events from multiple qualifying results.
   * Returns events grouped by team ID.
   */
  static createDrivingEventsFromQualifying(qualifyingFacts: TeamDrivingQualifyingFactsDto): Map<string, TeamRatingEvent[]> {
    const eventsByTeam = new Map<string, TeamRatingEvent[]>();

    for (const result of qualifyingFacts.results) {
      const events = this.createFromQualifying({
        teamId: result.teamId,
        qualifyingPosition: result.qualifyingPosition,
        fieldSize: result.fieldSize,
        raceId: qualifyingFacts.raceId,
      });

      if (events.length > 0) {
        eventsByTeam.set(result.teamId, events);
      }
    }

    return eventsByTeam;
  }

  /**
   * Create rating events from overtake/defense statistics.
   * Uses TeamDrivingRatingCalculator for overtake calculations.
   */
  static createFromOvertakeStats(input: {
    teamId: string;
    overtakes: number;
    successfulDefenses: number;
    raceId: string;
  }): TeamRatingEvent[] {
    const stats: TeamDrivingOvertakeStats = {
      teamId: input.teamId,
      overtakes: input.overtakes,
      successfulDefenses: input.successfulDefenses,
      raceId: input.raceId,
    };

    return TeamDrivingRatingCalculator.calculateFromOvertakeStats(stats);
  }

  /**
   * Create rating events from multiple overtake stats.
   * Returns events grouped by team ID.
   */
  static createDrivingEventsFromOvertakes(overtakeFacts: TeamDrivingOvertakeFactsDto): Map<string, TeamRatingEvent[]> {
    const eventsByTeam = new Map<string, TeamRatingEvent[]>();

    for (const result of overtakeFacts.results) {
      const events = this.createFromOvertakeStats({
        teamId: result.teamId,
        overtakes: result.overtakes,
        successfulDefenses: result.successfulDefenses,
        raceId: overtakeFacts.raceId,
      });

      if (events.length > 0) {
        eventsByTeam.set(result.teamId, events);
      }
    }

    return eventsByTeam;
  }

  /**
   * Create rating events from a penalty.
   * Generates both driving and adminTrust events.
   * Uses TeamDrivingReasonCode for validation.
   */
  static createFromPenalty(input: {
    teamId: string;
    penaltyType: 'minor' | 'major' | 'critical';
    severity: 'low' | 'medium' | 'high';
    incidentCount?: number;
  }): TeamRatingEvent[] {
    const now = new Date();
    const events: TeamRatingEvent[] = [];

    // Driving dimension penalty
    const drivingDelta = this.calculatePenaltyDelta(input.penaltyType, input.severity, 'driving');
    if (drivingDelta !== 0) {
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(drivingDelta),
          weight: 1,
          occurredAt: now,
          createdAt: now,
          source: { type: 'penalty', id: input.penaltyType },
          reason: {
            code: TeamDrivingReasonCode.create('RACE_INCIDENTS').value,
            description: `${input.penaltyType} penalty for driving violations`,
          },
          visibility: { public: true },
          version: 1,
        })
      );
    }

    // AdminTrust dimension penalty
    const adminDelta = this.calculatePenaltyDelta(input.penaltyType, input.severity, 'adminTrust');
    if (adminDelta !== 0) {
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(adminDelta),
          weight: 1,
          occurredAt: now,
          createdAt: now,
          source: { type: 'penalty', id: input.penaltyType },
          reason: {
            code: 'PENALTY_ADMIN',
            description: `${input.penaltyType} penalty for rule violations`,
          },
          visibility: { public: true },
          version: 1,
        })
      );
    }

    return events;
  }

  /**
   * Create rating events from a vote outcome.
   * Generates adminTrust events.
   */
  static createFromVote(input: {
    teamId: string;
    outcome: 'positive' | 'negative';
    voteCount: number;
    eligibleVoterCount: number;
    percentPositive: number;
  }): TeamRatingEvent[] {
    const now = new Date();
    const events: TeamRatingEvent[] = [];

    // Calculate delta based on vote outcome
    const delta = this.calculateVoteDelta(
      input.outcome,
      input.eligibleVoterCount,
      input.voteCount,
      input.percentPositive
    );

    if (delta !== 0) {
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(delta),
          weight: input.voteCount, // Weight by number of votes
          occurredAt: now,
          createdAt: now,
          source: { type: 'vote', id: 'admin_vote' },
          reason: {
            code: input.outcome === 'positive' ? 'VOTE_POSITIVE' : 'VOTE_NEGATIVE',
            description: `Admin vote outcome: ${input.outcome}`,
          },
          visibility: { public: true },
          version: 1,
        })
      );
    }

    return events;
  }

  /**
   * Create rating events from an admin action.
   * Generates adminTrust events.
   */
  static createFromAdminAction(input: {
    teamId: string;
    actionType: 'bonus' | 'penalty' | 'warning';
    severity?: 'low' | 'medium' | 'high';
  }): TeamRatingEvent[] {
    const now = new Date();
    const events: TeamRatingEvent[] = [];

    if (input.actionType === 'bonus') {
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(5),
          weight: 1,
          occurredAt: now,
          createdAt: now,
          source: { type: 'adminAction', id: 'bonus' },
          reason: {
            code: 'ADMIN_BONUS',
            description: 'Admin bonus for positive contribution',
          },
          visibility: { public: true },
          version: 1,
        })
      );
    } else if (input.actionType === 'penalty') {
      const delta = input.severity === 'high' ? -15 : input.severity === 'medium' ? -10 : -5;
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(delta),
          weight: 1,
          occurredAt: now,
          createdAt: now,
          source: { type: 'adminAction', id: 'penalty' },
          reason: {
            code: 'ADMIN_PENALTY',
            description: `Admin penalty (${input.severity} severity)`,
          },
          visibility: { public: true },
          version: 1,
        })
      );
    } else if (input.actionType === 'warning') {
      events.push(
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: input.teamId,
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(3),
          weight: 1,
          occurredAt: now,
          createdAt: now,
          source: { type: 'adminAction', id: 'warning' },
          reason: {
            code: 'ADMIN_WARNING_RESPONSE',
            description: 'Response to admin warning',
          },
          visibility: { public: true },
          version: 1,
        })
      );
    }

    return events;
  }

  // Private helper methods

  private static calculatePenaltyDelta(
    penaltyType: 'minor' | 'major' | 'critical',
    severity: 'low' | 'medium' | 'high',
    dimension: 'driving' | 'adminTrust'
  ): number {
    const baseValues = {
      minor: { driving: -5, adminTrust: -3 },
      major: { driving: -10, adminTrust: -8 },
      critical: { driving: -20, adminTrust: -15 },
    };

    const severityMultipliers = {
      low: 1,
      medium: 1.5,
      high: 2,
    };

    const base = baseValues[penaltyType][dimension];
    const multiplier = severityMultipliers[severity];

    return Math.round(base * multiplier);
  }

  private static calculateVoteDelta(
    outcome: 'positive' | 'negative',
    eligibleVoterCount: number,
    voteCount: number,
    percentPositive: number
  ): number {
    if (voteCount === 0) return 0;

    const participationRate = voteCount / eligibleVoterCount;
    const strength = (percentPositive / 100) * 2 - 1; // -1 to +1

    // Base delta of +/- 10, scaled by participation and strength
    const baseDelta = outcome === 'positive' ? 10 : -10;
    const scaledDelta = baseDelta * participationRate * (0.5 + Math.abs(strength) * 0.5);

    return Math.round(scaledDelta * 10) / 10;
  }
}