// Demo module for generating test data and running simulations

use crate::glicko::{GlickoRating, Glicko2Calculator, calculate_weighted_score};
use fake::faker::name::en::Name;
use fake::Fake;
extern crate rand;
use rand::Rng;
use rand::seq::SliceRandom;

#[derive(Debug, Clone)]
pub struct Player {
    pub name: String,
    pub email: String,
    pub singles: GlickoRating,
    pub doubles: GlickoRating,
    pub true_skill: f64, // Hidden skill level for simulation
}

impl Player {
    pub fn new_random() -> Self {
        let mut rng = rand::thread_rng();
        let name: String = Name().fake();
        let email = format!("{}@example.com", name.to_lowercase().replace(' ', "."));
        
        // Random true skill between 1200-1800
        let true_skill = rng.gen_range(1200.0..1800.0);
        
        Self {
            name,
            email,
            singles: GlickoRating::new_player(),
            doubles: GlickoRating::new_player(),
            true_skill,
        }
    }
}

#[derive(Debug)]
pub struct Match {
    pub match_type: MatchType,
    pub team1: Vec<usize>, // Player indices
    pub team2: Vec<usize>,
    pub team1_score: i32,
    pub team2_score: i32,
}

#[derive(Debug, Clone, Copy)]
pub enum MatchType {
    Singles,
    Doubles,
}

/// Simulate a match outcome based on true skill levels
pub fn simulate_match(
    team1_skills: &[f64],
    team2_skills: &[f64],
) -> (i32, i32) {
    let mut rng = rand::thread_rng();
    
    // Team skill is average
    let team1_avg: f64 = team1_skills.iter().sum::<f64>() / team1_skills.len() as f64;
    let team2_avg: f64 = team2_skills.iter().sum::<f64>() / team2_skills.len() as f64;
    
    // Win probability based on skill difference
    let skill_diff = team1_avg - team2_avg;
    let win_prob = 1.0 / (1.0 + 10_f64.powf(-skill_diff / 400.0));
    
    // Determine winner
    let team1_wins = rng.gen::<f64>() < win_prob;
    
    // Generate score (pickleball to 11)
    if team1_wins {
        let margin = rng.gen_range(1..10); // 1-9 point margin
        let team2_score = 11 - margin;
        (11, team2_score)
    } else {
        let margin = rng.gen_range(1..10);
        let team1_score = 11 - margin;
        (team1_score, 11)
    }
}

/// Generate a session of matches
pub fn generate_session(players: &mut [Player], num_matches: usize) -> Vec<Match> {
    let mut rng = rand::thread_rng();
    let mut matches = Vec::new();
    let calc = Glicko2Calculator::new();
    
    for _ in 0..num_matches {
        // Randomly choose singles or doubles
        let match_type = if rng.gen_bool(0.5) {
            MatchType::Singles
        } else {
            MatchType::Doubles
        };
        
        let match_result = match match_type {
            MatchType::Singles => {
                // Pick 2 random players
                let p1_idx = rng.gen_range(0..players.len());
                let mut p2_idx = rng.gen_range(0..players.len());
                while p2_idx == p1_idx {
                    p2_idx = rng.gen_range(0..players.len());
                }
                
                let (team1_score, team2_score) = simulate_match(
                    &[players[p1_idx].true_skill],
                    &[players[p2_idx].true_skill],
                );
                
                // Update ratings with performance-based weighting
                let p1_outcome = calculate_weighted_score(
                    players[p1_idx].singles.rating,
                    players[p2_idx].singles.rating,
                    team1_score,
                    team2_score
                );
                
                let p2_outcome = calculate_weighted_score(
                    players[p2_idx].singles.rating,
                    players[p1_idx].singles.rating,
                    team2_score,
                    team1_score
                );
                
                players[p1_idx].singles = calc.update_rating(
                    &players[p1_idx].singles,
                    &[(players[p2_idx].singles, p1_outcome)],
                );
                
                players[p2_idx].singles = calc.update_rating(
                    &players[p2_idx].singles,
                    &[(players[p1_idx].singles, p2_outcome)],
                );
                
                Match {
                    match_type,
                    team1: vec![p1_idx],
                    team2: vec![p2_idx],
                    team1_score,
                    team2_score,
                }
            }
            MatchType::Doubles => {
                // Pick 4 random players
                let mut indices: Vec<usize> = (0..players.len()).collect();
                indices.shuffle(&mut rng);
                let team1_indices = vec![indices[0], indices[1]];
                let team2_indices = vec![indices[2], indices[3]];
                
                let team1_skills: Vec<f64> = team1_indices.iter()
                    .map(|&i| players[i].true_skill)
                    .collect();
                let team2_skills: Vec<f64> = team2_indices.iter()
                    .map(|&i| players[i].true_skill)
                    .collect();
                
                let (team1_score, team2_score) = simulate_match(&team1_skills, &team2_skills);
                
                // Update doubles ratings (simplified - each player vs team average)
                for &idx in &team1_indices {
                    let avg_opponent_rating = team2_indices.iter().map(|&i| players[i].doubles.rating).sum::<f64>() / 2.0;
                    let outcome = calculate_weighted_score(
                        players[idx].doubles.rating,
                        avg_opponent_rating,
                        team1_score,
                        team2_score
                    );
                    
                    // Simulate vs average opponent
                    let avg_opponent = GlickoRating {
                        rating: avg_opponent_rating,
                        rd: team2_indices.iter().map(|&i| players[i].doubles.rd).sum::<f64>() / 2.0,
                        volatility: 0.06,
                    };
                    
                    players[idx].doubles = calc.update_rating(
                        &players[idx].doubles,
                        &[(avg_opponent, outcome)],
                    );
                }
                
                for &idx in &team2_indices {
                    let avg_opponent_rating = team1_indices.iter().map(|&i| players[i].doubles.rating).sum::<f64>() / 2.0;
                    let outcome = calculate_weighted_score(
                        players[idx].doubles.rating,
                        avg_opponent_rating,
                        team2_score,
                        team1_score
                    );
                    
                    let avg_opponent = GlickoRating {
                        rating: avg_opponent_rating,
                        rd: team1_indices.iter().map(|&i| players[i].doubles.rd).sum::<f64>() / 2.0,
                        volatility: 0.06,
                    };
                    
                    players[idx].doubles = calc.update_rating(
                        &players[idx].doubles,
                        &[(avg_opponent, outcome)],
                    );
                }
                
                Match {
                    match_type,
                    team1: team1_indices,
                    team2: team2_indices,
                    team1_score,
                    team2_score,
                }
            }
        };
        
        matches.push(match_result);
    }
    
    matches
}

/// Print session summary
pub fn print_summary(players: &[Player], matches: &[Match]) {
    println!("\n🏓 Session Summary");
    println!("==================");
    println!("\n{} matches played\n", matches.len());
    
    println!("Match Results:");
    for (i, m) in matches.iter().enumerate() {
        match m.match_type {
            MatchType::Singles => {
                let p1 = &players[m.team1[0]];
                let p2 = &players[m.team2[0]];
                let winner = if m.team1_score > m.team2_score { &p1.name } else { &p2.name };
                println!(
                    "  {}. Singles: {} vs {} → {} wins {}-{}",
                    i + 1,
                    p1.name,
                    p2.name,
                    winner,
                    m.team1_score.max(m.team2_score),
                    m.team1_score.min(m.team2_score)
                );
            }
            MatchType::Doubles => {
                let t1_names: Vec<&str> = m.team1.iter().map(|&i| players[i].name.as_str()).collect();
                let t2_names: Vec<&str> = m.team2.iter().map(|&i| players[i].name.as_str()).collect();
                let winner = if m.team1_score > m.team2_score { "Team 1" } else { "Team 2" };
                println!(
                    "  {}. Doubles: {} vs {} → {} wins {}-{}",
                    i + 1,
                    t1_names.join(" & "),
                    t2_names.join(" & "),
                    winner,
                    m.team1_score.max(m.team2_score),
                    m.team1_score.min(m.team2_score)
                );
            }
        }
    }
    
    println!("\n📊 Singles Leaderboard:");
    let mut singles_sorted = players.to_vec();
    singles_sorted.sort_by(|a, b| b.singles.rating.partial_cmp(&a.singles.rating).unwrap());
    for (i, p) in singles_sorted.iter().take(10).enumerate() {
        println!(
            "  {}. {} - {:.1} (RD: {:.1}, σ: {:.3})",
            i + 1,
            p.name,
            p.singles.rating,
            p.singles.rd,
            p.singles.volatility
        );
    }
    
    println!("\n📊 Doubles Leaderboard:");
    let mut doubles_sorted = players.to_vec();
    doubles_sorted.sort_by(|a, b| b.doubles.rating.partial_cmp(&a.doubles.rating).unwrap());
    for (i, p) in doubles_sorted.iter().take(10).enumerate() {
        println!(
            "  {}. {} - {:.1} (RD: {:.1}, σ: {:.3})",
            i + 1,
            p.name,
            p.doubles.rating,
            p.doubles.rd,
            p.doubles.volatility
        );
    }
}
