use super::rating::GlickoRating;

/// Calculate team rating from two partners (average approach)
/// Returns: (team_mu, team_phi) in Glicko-2 scale
pub fn calculate_team_rating(
    partner1: &GlickoRating,
    partner2: &GlickoRating,
) -> (f64, f64) {
    let (mu1, phi1, _) = partner1.to_glicko2_scale();
    let (mu2, phi2, _) = partner2.to_glicko2_scale();
    
    let team_mu = (mu1 + mu2) / 2.0;
    let team_phi = ((phi1.powi(2) + phi2.powi(2)) / 2.0).sqrt();
    
    (team_mu, team_phi)
}

/// Calculate effective opponent rating for a player in doubles
/// This personalizes the rating adjustment based on partner strength
/// 
/// Formula: Effective Opponent = Opp1_rating + Opp2_rating - Teammate_rating
/// 
/// This makes intuitive sense:
/// - If opponents are strong, effective opponent rating is higher
/// - If your teammate is strong, effective opponent rating is lower (teammate helped)
/// - If your teammate is weak, effective opponent rating is higher (you did more work)
///
/// Returns: The effective opponent rating (in display scale, e.g., 1400-1600)
pub fn calculate_effective_opponent_rating(
    opponent1_rating: f64,
    opponent2_rating: f64,
    teammate_rating: f64,
) -> f64 {
    opponent1_rating + opponent2_rating - teammate_rating
}

/// Calculate effective opponent as a GlickoRating struct
/// Uses the effective rating and interpolates RD/volatility
pub fn calculate_effective_opponent(
    opponent1: &GlickoRating,
    opponent2: &GlickoRating,
    teammate: &GlickoRating,
) -> GlickoRating {
    let effective_rating = calculate_effective_opponent_rating(
        opponent1.rating,
        opponent2.rating,
        teammate.rating,
    );
    
    // For RD, use average of opponents (they're the collective threat)
    let effective_rd = (opponent1.rd + opponent2.rd) / 2.0;
    
    // For volatility, use average opponent volatility
    let effective_volatility = (opponent1.volatility + opponent2.volatility) / 2.0;
    
    GlickoRating {
        rating: effective_rating,
        rd: effective_rd,
        volatility: effective_volatility,
    }
}

/// Distribute rating change between partners based on RD
/// More uncertain (higher RD) players get more weight because they should update faster
/// This reflects the principle that ratings with higher uncertainty should be adjusted more
/// aggressively to converge to their true skill level.
pub fn distribute_rating_change(
    partner1_rd: f64,
    partner2_rd: f64,
    team_change: f64,
) -> (f64, f64) {
    // Higher RD → more uncertain → deserves more change
    let weight1 = partner1_rd.powi(2);
    let weight2 = partner2_rd.powi(2);
    let total_weight = weight1 + weight2;
    
    (
        team_change * (weight1 / total_weight),
        team_change * (weight2 / total_weight),
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_team_rating() {
        let p1 = GlickoRating { rating: 1600.0, rd: 200.0, volatility: 0.06 };
        let p2 = GlickoRating { rating: 1400.0, rd: 200.0, volatility: 0.06 };
        let (team_mu, _) = calculate_team_rating(&p1, &p2);
        // Team rating should be ~1500 (average)
        let team_rating = team_mu * 173.7178 + 1500.0;
        assert!((team_rating - 1500.0).abs() < 1.0);
        println!("Team rating: {}", team_rating);
    }
    
    #[test]
    fn test_distribution() {
        let (c1, c2) = distribute_rating_change(100.0, 200.0, 10.0);
        // Higher RD (200) should get more change (now correct!)
        assert!(c2 > c1);
        // Should sum to total change
        assert!((c1 + c2 - 10.0).abs() < 0.001);
        println!("Distribution: {} / {} (total: {})", c1, c2, c1 + c2);
    }
    
    #[test]
    fn test_effective_opponent_equal_teams() {
        // Both teams equally matched
        // Opp1: 1500, Opp2: 1500, Teammate: 1500
        // Effective opponent = 1500 + 1500 - 1500 = 1500
        let eff = calculate_effective_opponent_rating(1500.0, 1500.0, 1500.0);
        assert!((eff - 1500.0).abs() < 0.001);
        println!("Equal teams: {}", eff);
    }
    
    #[test]
    fn test_effective_opponent_strong_teammate() {
        // Strong teammates make it "easier" - lower effective opponent
        // Opp1: 1500, Opp2: 1500, Teammate: 1600
        // Effective opponent = 1500 + 1500 - 1600 = 1400
        let eff = calculate_effective_opponent_rating(1500.0, 1500.0, 1600.0);
        assert!((eff - 1400.0).abs() < 0.001);
        println!("Strong teammate (1600 vs 1500/1500): effective = {}", eff);
    }
    
    #[test]
    fn test_effective_opponent_weak_teammate() {
        // Weak teammates make it "harder" - higher effective opponent
        // Opp1: 1500, Opp2: 1500, Teammate: 1400
        // Effective opponent = 1500 + 1500 - 1400 = 1600
        let eff = calculate_effective_opponent_rating(1500.0, 1500.0, 1400.0);
        assert!((eff - 1600.0).abs() < 0.001);
        println!("Weak teammate (1400 vs 1500/1500): effective = {}", eff);
    }
    
    #[test]
    fn test_effective_opponent_struct() {
        let opp1 = GlickoRating { rating: 1500.0, rd: 100.0, volatility: 0.06 };
        let opp2 = GlickoRating { rating: 1600.0, rd: 150.0, volatility: 0.07 };
        let teammate = GlickoRating { rating: 1400.0, rd: 200.0, volatility: 0.08 };
        
        let eff = calculate_effective_opponent(&opp1, &opp2, &teammate);
        
        // Rating: 1500 + 1600 - 1400 = 1700
        assert!((eff.rating - 1700.0).abs() < 0.001);
        // RD: (100 + 150) / 2 = 125
        assert!((eff.rd - 125.0).abs() < 0.001);
        // Volatility: (0.06 + 0.07) / 2 = 0.065
        assert!((eff.volatility - 0.065).abs() < 0.001);
        
        println!("Effective opponent struct: {:.0} (RD: {:.0})", eff.rating, eff.rd);
    }
}
