//! Diagnostic metrics for game state analysis.
//!
//! Provides structured metrics for evaluating board advantage, tempo,
//! resource efficiency, and combat effectiveness.

use super::collector::TurnSnapshot;

/// Board advantage metric.
///
/// Measures who's ahead at a given point in the game by combining
/// life differential, board attack differential, and board health differential.
#[derive(Clone, Debug, Default)]
pub struct BoardAdvantage {
    /// Life differential (P1 - P2).
    pub life_diff: i32,
    /// Board attack differential (P1 - P2).
    pub attack_diff: i32,
    /// Board health differential (P1 - P2).
    pub health_diff: i32,
    /// Creature count differential (P1 - P2).
    pub creature_diff: i32,
}

impl BoardAdvantage {
    /// Create a new board advantage metric from a snapshot.
    pub fn from_snapshot(snapshot: &TurnSnapshot) -> Self {
        Self {
            life_diff: snapshot.p1_life - snapshot.p2_life,
            attack_diff: snapshot.p1_total_attack - snapshot.p2_total_attack,
            health_diff: snapshot.p1_total_health - snapshot.p2_total_health,
            creature_diff: snapshot.p1_creatures as i32 - snapshot.p2_creatures as i32,
        }
    }

    /// Calculate the composite advantage score.
    ///
    /// Returns a normalized score where positive values favor P1 and
    /// negative values favor P2.
    pub fn score(&self) -> f64 {
        (self.life_diff as f64 + self.attack_diff as f64 + self.health_diff as f64) / 3.0
    }

    /// Determine which player is ahead.
    ///
    /// Returns Some(true) if P1 is ahead, Some(false) if P2 is ahead,
    /// or None if roughly equal (within threshold).
    pub fn leader(&self, threshold: f64) -> Option<bool> {
        let score = self.score();
        if score > threshold {
            Some(true) // P1 ahead
        } else if score < -threshold {
            Some(false) // P2 ahead
        } else {
            None // Even
        }
    }
}

/// Tempo metrics tracking initiative and early game events.
#[derive(Clone, Debug, Default)]
pub struct TempoMetrics {
    /// Turn when P1 played their first creature.
    pub p1_first_creature_turn: Option<u32>,
    /// Turn when P2 played their first creature.
    pub p2_first_creature_turn: Option<u32>,
}

impl TempoMetrics {
    /// Check who played a creature first.
    ///
    /// Returns Some(true) if P1 played first, Some(false) if P2,
    /// or None if data unavailable.
    pub fn first_creature_player(&self) -> Option<bool> {
        match (self.p1_first_creature_turn, self.p2_first_creature_turn) {
            (Some(t1), Some(t2)) => Some(t1 <= t2),
            (Some(_), None) => Some(true),
            (None, Some(_)) => Some(false),
            (None, None) => None,
        }
    }
}

/// Resource efficiency metrics.
///
/// Tracks how efficiently players convert essence into board presence.
#[derive(Clone, Debug, Default)]
pub struct ResourceEfficiency {
    /// Total essence spent by P1.
    pub p1_essence_spent: u32,
    /// Total essence spent by P2.
    pub p2_essence_spent: u32,
    /// Total board impact generated by P1 (attack + health of played creatures).
    pub p1_board_impact: i32,
    /// Total board impact generated by P2.
    pub p2_board_impact: i32,
}

impl ResourceEfficiency {
    /// Calculate P1's efficiency (board impact per essence spent).
    pub fn p1_efficiency(&self) -> f64 {
        if self.p1_essence_spent == 0 {
            0.0
        } else {
            self.p1_board_impact as f64 / self.p1_essence_spent as f64
        }
    }

    /// Calculate P2's efficiency (board impact per essence spent).
    pub fn p2_efficiency(&self) -> f64 {
        if self.p2_essence_spent == 0 {
            0.0
        } else {
            self.p2_board_impact as f64 / self.p2_essence_spent as f64
        }
    }

    /// Calculate efficiency differential (P1 - P2).
    pub fn efficiency_diff(&self) -> f64 {
        self.p1_efficiency() - self.p2_efficiency()
    }
}

/// Combat efficiency metrics.
///
/// Tracks damage dealt and taken by each player for trade analysis.
#[derive(Clone, Debug, Default)]
pub struct CombatEfficiency {
    /// Total damage dealt to enemy face by P1.
    pub p1_face_damage: u32,
    /// Total damage dealt to enemy face by P2.
    pub p2_face_damage: u32,
    /// Total damage dealt to enemy creatures by P1.
    pub p1_creature_damage: u32,
    /// Total damage dealt to enemy creatures by P2.
    pub p2_creature_damage: u32,
    /// Creatures killed by P1.
    pub p1_creatures_killed: u32,
    /// Creatures killed by P2.
    pub p2_creatures_killed: u32,
    /// Creatures lost by P1.
    pub p1_creatures_lost: u32,
    /// Creatures lost by P2.
    pub p2_creatures_lost: u32,
}

impl CombatEfficiency {
    /// Calculate P1's trade ratio (creatures killed / creatures lost).
    ///
    /// Returns:
    /// - `None` if no creatures were killed or lost (no trades)
    /// - `Some(f64::INFINITY)` if killed creatures but lost none (perfect)
    /// - `Some(ratio)` otherwise
    pub fn p1_trade_ratio(&self) -> Option<f64> {
        match (self.p1_creatures_killed, self.p1_creatures_lost) {
            (0, 0) => None,                    // No trades occurred
            (_, 0) => Some(f64::INFINITY), // Perfect: killed without losing
            (kills, losses) => Some(kills as f64 / losses as f64),
        }
    }

    /// Calculate P2's trade ratio (creatures killed / creatures lost).
    ///
    /// Returns:
    /// - `None` if no creatures were killed or lost (no trades)
    /// - `Some(f64::INFINITY)` if killed creatures but lost none (perfect)
    /// - `Some(ratio)` otherwise
    pub fn p2_trade_ratio(&self) -> Option<f64> {
        match (self.p2_creatures_killed, self.p2_creatures_lost) {
            (0, 0) => None,                    // No trades occurred
            (_, 0) => Some(f64::INFINITY), // Perfect: killed without losing
            (kills, losses) => Some(kills as f64 / losses as f64),
        }
    }

    /// Calculate P1's total damage output.
    pub fn p1_total_damage(&self) -> u32 {
        self.p1_face_damage + self.p1_creature_damage
    }

    /// Calculate P2's total damage output.
    pub fn p2_total_damage(&self) -> u32 {
        self.p2_face_damage + self.p2_creature_damage
    }

    /// Calculate damage efficiency (damage dealt vs received).
    pub fn p1_damage_efficiency(&self) -> f64 {
        let dealt = self.p1_total_damage() as f64;
        let received = self.p2_total_damage() as f64;
        if received == 0.0 {
            dealt
        } else {
            dealt / received
        }
    }
}

/// Aggregated metrics per turn for trend analysis.
#[derive(Clone, Debug, Default)]
pub struct TurnMetrics {
    /// Turn number.
    pub turn: u32,
    /// Board advantage at this turn.
    pub board_advantage: BoardAdvantage,
    /// Cumulative essence spent by P1 up to this turn.
    pub p1_cumulative_essence: u32,
    /// Cumulative essence spent by P2 up to this turn.
    pub p2_cumulative_essence: u32,
}

impl TurnMetrics {
    /// Create turn metrics from a snapshot.
    pub fn from_snapshot(snapshot: &TurnSnapshot, p1_spent: u32, p2_spent: u32) -> Self {
        Self {
            turn: snapshot.turn,
            board_advantage: BoardAdvantage::from_snapshot(snapshot),
            p1_cumulative_essence: p1_spent,
            p2_cumulative_essence: p2_spent,
        }
    }
}

/// Per-game metric summary.
#[derive(Clone, Debug, Default)]
pub struct GameMetrics {
    /// Per-turn metrics throughout the game.
    pub turn_metrics: Vec<TurnMetrics>,
    /// Tempo metrics for the game.
    pub tempo: TempoMetrics,
    /// Resource efficiency metrics.
    pub resource_efficiency: ResourceEfficiency,
    /// Combat efficiency metrics.
    pub combat_efficiency: CombatEfficiency,
    /// Average board advantage score (positive = P1 ahead).
    pub avg_board_advantage: f64,
    /// Number of turns P1 was ahead.
    pub turns_p1_ahead: u32,
    /// Number of turns P2 was ahead.
    pub turns_p2_ahead: u32,
    /// Number of turns roughly even.
    pub turns_even: u32,
    /// Winner of the game (None = draw).
    pub winner: Option<crate::types::PlayerId>,
    /// Who got first blood (first to deal damage to opponent).
    pub first_blood: Option<crate::types::PlayerId>,
}

impl GameMetrics {
    /// Calculate from turn metrics with a threshold for "even" determination.
    pub fn summarize(turn_metrics: &[TurnMetrics], threshold: f64) -> Self {
        let mut turns_p1_ahead = 0;
        let mut turns_p2_ahead = 0;
        let mut turns_even = 0;
        let mut total_advantage = 0.0;

        for tm in turn_metrics {
            let score = tm.board_advantage.score();
            total_advantage += score;

            match tm.board_advantage.leader(threshold) {
                Some(true) => turns_p1_ahead += 1,
                Some(false) => turns_p2_ahead += 1,
                None => turns_even += 1,
            }
        }

        let avg_board_advantage = if turn_metrics.is_empty() {
            0.0
        } else {
            total_advantage / turn_metrics.len() as f64
        };

        Self {
            turn_metrics: turn_metrics.to_vec(),
            avg_board_advantage,
            turns_p1_ahead,
            turns_p2_ahead,
            turns_even,
            ..Default::default()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::PlayerId;

    fn create_test_snapshot(turn: u32, p1_life: i32, p2_life: i32) -> TurnSnapshot {
        TurnSnapshot {
            turn,
            active_player: PlayerId::PLAYER_ONE,
            p1_life,
            p2_life,
            p1_creatures: 2,
            p2_creatures: 1,
            p1_total_attack: 5,
            p2_total_attack: 3,
            p1_total_health: 8,
            p2_total_health: 4,
            p1_hand_size: 3,
            p2_hand_size: 4,
            p1_essence: 3,
            p2_essence: 2,
            p1_max_essence: 4,
            p2_max_essence: 4,
            p1_action_points: 3,
            p2_action_points: 3,
        }
    }

    #[test]
    fn test_board_advantage_from_snapshot() {
        let snapshot = create_test_snapshot(5, 25, 28);
        let advantage = BoardAdvantage::from_snapshot(&snapshot);

        assert_eq!(advantage.life_diff, -3);
        assert_eq!(advantage.attack_diff, 2);
        assert_eq!(advantage.health_diff, 4);
        assert_eq!(advantage.creature_diff, 1);
    }

    #[test]
    fn test_board_advantage_score() {
        let snapshot = create_test_snapshot(5, 30, 30);
        let advantage = BoardAdvantage::from_snapshot(&snapshot);

        // (0 + 2 + 4) / 3 = 2.0
        assert!((advantage.score() - 2.0).abs() < 0.01);
    }

    #[test]
    fn test_board_advantage_leader() {
        let mut advantage = BoardAdvantage {
            life_diff: 10,
            attack_diff: 5,
            health_diff: 5,
            creature_diff: 1,
        };

        assert_eq!(advantage.leader(1.0), Some(true)); // P1 clearly ahead

        advantage.life_diff = -10;
        advantage.attack_diff = -5;
        advantage.health_diff = -5;
        assert_eq!(advantage.leader(1.0), Some(false)); // P2 clearly ahead

        advantage.life_diff = 0;
        advantage.attack_diff = 0;
        advantage.health_diff = 0;
        assert_eq!(advantage.leader(1.0), None); // Even
    }

    #[test]
    fn test_tempo_metrics_first_creature() {
        let tempo = TempoMetrics {
            p1_first_creature_turn: Some(2),
            p2_first_creature_turn: Some(3),
        };

        assert_eq!(tempo.first_creature_player(), Some(true)); // P1 first

        let tempo2 = TempoMetrics {
            p1_first_creature_turn: Some(3),
            p2_first_creature_turn: Some(2),
        };

        assert_eq!(tempo2.first_creature_player(), Some(false)); // P2 first
    }

    #[test]
    fn test_resource_efficiency() {
        let efficiency = ResourceEfficiency {
            p1_essence_spent: 10,
            p2_essence_spent: 10,
            p1_board_impact: 25,
            p2_board_impact: 20,
        };

        assert!((efficiency.p1_efficiency() - 2.5).abs() < 0.01);
        assert!((efficiency.p2_efficiency() - 2.0).abs() < 0.01);
        assert!((efficiency.efficiency_diff() - 0.5).abs() < 0.01);
    }

    #[test]
    fn test_combat_efficiency_trade_ratio() {
        let combat = CombatEfficiency {
            p1_creatures_killed: 4,
            p1_creatures_lost: 2,
            p2_creatures_killed: 2,
            p2_creatures_lost: 4,
            ..Default::default()
        };

        assert!((combat.p1_trade_ratio().unwrap() - 2.0).abs() < 0.01);
        assert!((combat.p2_trade_ratio().unwrap() - 0.5).abs() < 0.01);

        // Test infinity case (perfect trade)
        let perfect = CombatEfficiency {
            p1_creatures_killed: 3,
            p1_creatures_lost: 0,
            ..Default::default()
        };
        assert!(perfect.p1_trade_ratio().unwrap().is_infinite());

        // Test None case (no trades)
        let no_trades = CombatEfficiency::default();
        assert!(no_trades.p1_trade_ratio().is_none());
    }

    #[test]
    fn test_game_metrics_summarize() {
        let snapshots = [
            create_test_snapshot(1, 30, 30),
            create_test_snapshot(2, 28, 30),
            create_test_snapshot(3, 26, 28),
        ];

        let turn_metrics: Vec<TurnMetrics> = snapshots
            .iter()
            .map(|s| TurnMetrics::from_snapshot(s, 0, 0))
            .collect();

        let game_metrics = GameMetrics::summarize(&turn_metrics, 1.0);

        assert_eq!(game_metrics.turn_metrics.len(), 3);
        // All turns P1 should be ahead (positive board health/attack advantage)
        assert!(game_metrics.turns_p1_ahead > 0);
    }
}
