/**
 * Expected Value (EV) Calculator
 *
 * Calculates fantasy points expected value based on probability distributions
 * and league-specific scoring rules.
 */

/**
 * Scoring rules for a fantasy league season
 */
export interface ScoringRules {
  pointsFor1st: number;
  pointsFor2nd: number;
  pointsFor3rd: number;
  pointsFor4th: number;
  pointsFor5th: number;
  pointsFor6th: number;
  pointsFor7th: number;
  pointsFor8th: number;
}

/**
 * Probability distribution for a participant's placements
 * All values should be decimals (0-1) and sum to ~1.0
 */
export interface ProbabilityDistribution {
  probFirst: number;   // e.g., 0.155 means 15.5%
  probSecond: number;
  probThird: number;
  probFourth: number;
  probFifth: number;
  probSixth: number;
  probSeventh: number;
  probEighth: number;
}

/**
 * Calculate Expected Value for a participant
 *
 * EV = Σ (probability × points) for each placement
 *
 * @param probabilities - Probability distribution (decimals 0-1)
 * @param scoringRules - League's point values for each placement
 * @returns Expected value (average projected points)
 *
 * @example
 * ```ts
 * const ev = calculateEV(
 *   { probFirst: 0.20, probSecond: 0.15, probThird: 0.12, ..., probEighth: 0.05 },
 *   { pointsFor1st: 100, pointsFor2nd: 70, ..., pointsFor8th: 15 }
 * );
 * // Returns: 45.5 (expected points)
 * ```
 */
export function calculateEV(
  probabilities: ProbabilityDistribution,
  scoringRules: ScoringRules
): number {
  const ev =
    probabilities.probFirst * scoringRules.pointsFor1st +
    probabilities.probSecond * scoringRules.pointsFor2nd +
    probabilities.probThird * scoringRules.pointsFor3rd +
    probabilities.probFourth * scoringRules.pointsFor4th +
    probabilities.probFifth * scoringRules.pointsFor5th +
    probabilities.probSixth * scoringRules.pointsFor6th +
    probabilities.probSeventh * scoringRules.pointsFor7th +
    probabilities.probEighth * scoringRules.pointsFor8th;

  return ev;
}

/**
 * Validate that probability distribution sums to approximately 1.0
 * (allows small rounding errors)
 *
 * @param probabilities - Probability distribution to validate
 * @param tolerance - Acceptable deviation from 1.0 (default 0.01)
 * @returns true if valid, false otherwise
 */
export function validateProbabilities(
  probabilities: ProbabilityDistribution,
  tolerance = 0.01
): boolean {
  const sum =
    probabilities.probFirst +
    probabilities.probSecond +
    probabilities.probThird +
    probabilities.probFourth +
    probabilities.probFifth +
    probabilities.probSixth +
    probabilities.probSeventh +
    probabilities.probEighth;

  return Math.abs(sum - 1.0) <= tolerance;
}

/**
 * Normalize probabilities to sum to exactly 1.0
 * Useful when importing odds or dealing with rounding errors
 *
 * @param probabilities - Probability distribution to normalize
 * @returns Normalized probabilities that sum to 1.0
 */
export function normalizeProbabilities(
  probabilities: ProbabilityDistribution
): ProbabilityDistribution {
  const sum =
    probabilities.probFirst +
    probabilities.probSecond +
    probabilities.probThird +
    probabilities.probFourth +
    probabilities.probFifth +
    probabilities.probSixth +
    probabilities.probSeventh +
    probabilities.probEighth;

  if (sum === 0) {
    // Avoid division by zero - return equal probabilities (1/8 = 0.125)
    return {
      probFirst: 0.125,
      probSecond: 0.125,
      probThird: 0.125,
      probFourth: 0.125,
      probFifth: 0.125,
      probSixth: 0.125,
      probSeventh: 0.125,
      probEighth: 0.125,
    };
  }

  const factor = 1.0 / sum;

  return {
    probFirst: Math.round(probabilities.probFirst * factor * 10000) / 10000,
    probSecond: Math.round(probabilities.probSecond * factor * 10000) / 10000,
    probThird: Math.round(probabilities.probThird * factor * 10000) / 10000,
    probFourth: Math.round(probabilities.probFourth * factor * 10000) / 10000,
    probFifth: Math.round(probabilities.probFifth * factor * 10000) / 10000,
    probSixth: Math.round(probabilities.probSixth * factor * 10000) / 10000,
    probSeventh: Math.round(probabilities.probSeventh * factor * 10000) / 10000,
    probEighth: Math.round(probabilities.probEighth * factor * 10000) / 10000,
  };
}

/**
 * Calculate projected total points for a team
 *
 * @param actualPoints - Points from finished participants
 * @param remainingParticipantEVs - Array of EV values for unfinished participants
 * @returns Object with actual, projected, and remaining count
 */
export function calculateProjectedTotal(
  actualPoints: number,
  remainingParticipantEVs: number[]
): {
  actualPoints: number;
  projectedPoints: number;
  participantsFinished: number;
  participantsRemaining: number;
} {
  const evSum = remainingParticipantEVs.reduce((sum, ev) => sum + ev, 0);
  const projectedPoints = actualPoints + evSum;

  return {
    actualPoints: Math.round(actualPoints * 100) / 100,
    projectedPoints: Math.round(projectedPoints * 100) / 100,
    participantsFinished: 0, // Caller should provide this
    participantsRemaining: remainingParticipantEVs.length,
  };
}
