//! Tests migrated from inline #[cfg(test)] modules in src/diagnostics/*.rs

use cardgame::decks::Faction;
use cardgame::diagnostics::{
    AggregatedStats, BoardAdvantage, CombatEfficiency, DiagnosticConfig, DiagnosticRunner,
    ExportFormat, GameDiagnostics, GameMetrics, ResourceEfficiency, TempoMetrics, TurnMetrics,
    TurnSnapshot,
};
use cardgame::engine::GameEngine;
use cardgame::execution::GameData;
use cardgame::types::PlayerId;

// ── helpers ──────────────────────────────────────────────────────────────────

fn load_test_data() -> GameData {
    GameData::load_default(true).expect("Failed to load test data")
}

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,
    }
}

fn create_test_diagnostic(winner: Option<PlayerId>, turns: u32, seed: u64) -> GameDiagnostics {
    GameDiagnostics {
        seed,
        winner,
        total_turns: turns,
        snapshots: vec![],
        first_damage_to_p1_turn: Some(2),
        first_damage_to_p2_turn: Some(3),
        first_creature_death_turn: Some(4),
        p1_actions: 10,
        p2_actions: 12,
        metrics: GameMetrics::default(),
        p1_play_card: 3,
        p1_attack: 4,
        p1_ability: 0,
        p1_end_turn: 3,
        p2_play_card: 4,
        p2_attack: 5,
        p2_ability: 0,
        p2_end_turn: 3,
    }
}

// ── collector.rs ─────────────────────────────────────────────────────────────

#[test]
fn test_turn_snapshot_capture() {
    let data = load_test_data();
    let decks = data.deck_registry.decks_for_faction(Faction::Argentum);
    let deck = (*decks.first().expect("No Argentum deck found")).clone();

    let mut engine = GameEngine::new(&data.card_db);
    engine.start_game(&deck, &deck, 42).unwrap();

    let snapshot = TurnSnapshot::capture(&engine);

    assert_eq!(snapshot.turn, 1);
    assert_eq!(snapshot.p1_life, 30);
    assert_eq!(snapshot.p2_life, 30);
}

#[test]
fn test_diagnostic_runner() {
    let data = load_test_data();
    let decks = data.deck_registry.decks_for_faction(Faction::Argentum);
    let deck = (*decks.first().expect("No Argentum deck found")).clone();

    let config = DiagnosticConfig::new(deck, 3).with_seed(42);
    let runner = DiagnosticRunner::new(&data.card_db);
    let results = runner.run(&config).expect("Diagnostic run should succeed");

    assert_eq!(results.len(), 3);
    for diag in &results {
        assert!(!diag.snapshots.is_empty());
        assert!(diag.total_turns > 0);
    }
}

#[test]
fn test_diagnostic_uses_commander() {
    let data = load_test_data();
    let decks = data.deck_registry.decks_for_faction(Faction::Argentum);
    let deck = (*decks.first().expect("No Argentum deck found")).clone();

    assert!(deck.commander > 0);

    let config = DiagnosticConfig::new(deck, 1).with_seed(42);
    let runner = DiagnosticRunner::new(&data.card_db);
    let results = runner.run(&config).expect("Diagnostic run should succeed");

    assert_eq!(results.len(), 1);
    assert!(results[0].total_turns > 0);
}

// ── analyzer.rs (diagnostics) ────────────────────────────────────────────────

#[test]
fn test_aggregated_stats_basic() {
    let games = vec![
        create_test_diagnostic(Some(PlayerId::PLAYER_ONE), 15, 1),
        create_test_diagnostic(Some(PlayerId::PLAYER_TWO), 20, 2),
        create_test_diagnostic(Some(PlayerId::PLAYER_ONE), 25, 3),
    ];

    let stats = AggregatedStats::analyze(&games);

    assert_eq!(stats.total_games, 3);
    assert_eq!(stats.p1_wins, 2);
    assert_eq!(stats.p2_wins, 1);
    assert!((stats.p1_win_rate() - 0.666).abs() < 0.01);
}

#[test]
fn test_game_length_buckets() {
    let games = vec![
        create_test_diagnostic(Some(PlayerId::PLAYER_ONE), 8, 1),
        create_test_diagnostic(Some(PlayerId::PLAYER_TWO), 15, 2),
        create_test_diagnostic(Some(PlayerId::PLAYER_ONE), 25, 3),
    ];

    let stats = AggregatedStats::analyze(&games);

    assert_eq!(stats.games_early, 1);
    assert_eq!(stats.games_mid, 1);
    assert_eq!(stats.games_late, 1);
    assert_eq!(stats.p1_wins_early, 1);
    assert_eq!(stats.p1_wins_mid, 0);
    assert_eq!(stats.p1_wins_late, 1);
}

// ── report.rs (diagnostics) ─────────────────────────────────────────────────

#[test]
fn test_print_report_empty_stats() {
    use cardgame::diagnostics::print_report;

    let stats = AggregatedStats::new();
    print_report(&stats);
}

// ── export.rs ────────────────────────────────────────────────────────────────

#[test]
fn test_export_format_parse() {
    assert_eq!(ExportFormat::parse("csv"), Some(ExportFormat::Csv));
    assert_eq!(ExportFormat::parse("JSON"), Some(ExportFormat::Json));
    assert_eq!(ExportFormat::parse("all"), Some(ExportFormat::All));
    assert_eq!(ExportFormat::parse("invalid"), None);
}

#[test]
fn test_aggregate_stats_export() {
    use cardgame::diagnostics::export::AggregateStatsExport;

    let stats = AggregatedStats::default();
    let export = AggregateStatsExport::from_stats(&stats);

    assert_eq!(export.total_games, 0);
    assert_eq!(export.p1_wins, 0);
}

// ── statistics.rs ────────────────────────────────────────────────────────────

#[test]
fn test_wilson_score_interval() {
    use cardgame::diagnostics::wilson_score_interval;

    let (lower, upper) = wilson_score_interval(50, 100, 0.95);
    assert!(lower < 0.5);
    assert!(upper > 0.5);
    assert!((lower - 0.4).abs() < 0.05);
    assert!((upper - 0.6).abs() < 0.05);
}

#[test]
fn test_wilson_score_edge_cases() {
    use cardgame::diagnostics::wilson_score_interval;

    let (lower, upper) = wilson_score_interval(100, 100, 0.95);
    assert!(lower > 0.95);
    assert!((upper - 1.0).abs() < 0.01);

    let (lower, upper) = wilson_score_interval(0, 100, 0.95);
    assert!(lower < 0.01);
    assert!(upper < 0.05);

    let (lower, upper) = wilson_score_interval(0, 0, 0.95);
    assert_eq!(lower, 0.0);
    assert_eq!(upper, 1.0);
}

#[test]
fn test_chi_square_test() {
    use cardgame::diagnostics::chi_square_test;

    let (chi_sq, p) = chi_square_test(50, 100, 0.5);
    assert!(chi_sq < 0.1);
    assert!(p > 0.05);

    let (chi_sq, p) = chi_square_test(70, 100, 0.5);
    assert!(chi_sq > 10.0);
    assert!(p < 0.01);
}

#[test]
fn test_percentile() {
    use cardgame::diagnostics::percentile;

    let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
    assert!((percentile(&values, 0.0).unwrap() - 1.0).abs() < 0.01);
    assert!((percentile(&values, 0.5).unwrap() - 3.0).abs() < 0.01);
    assert!((percentile(&values, 1.0).unwrap() - 5.0).abs() < 0.01);
}

#[test]
fn test_mean_variance() {
    use cardgame::diagnostics::mean_variance;

    let values = vec![2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
    let (mean, variance, n) = mean_variance(&values);

    assert_eq!(n, 8);
    assert!((mean - 5.0).abs() < 0.01);
    assert!((variance - 4.57).abs() < 0.1);
}

#[test]
fn test_pearson_correlation() {
    use cardgame::diagnostics::pearson_correlation;

    let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
    let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
    let r = pearson_correlation(&x, &y).unwrap();
    assert!((r - 1.0).abs() < 0.01);

    let y_neg = vec![10.0, 8.0, 6.0, 4.0, 2.0];
    let r_neg = pearson_correlation(&x, &y_neg).unwrap();
    assert!((r_neg + 1.0).abs() < 0.01);

    let y_zero = vec![5.0, 3.0, 7.0, 2.0, 8.0];
    let r_zero = pearson_correlation(&x, &y_zero).unwrap();
    assert!(r_zero.abs() < 0.5);
}

#[test]
fn test_significance_level() {
    use cardgame::diagnostics::SignificanceLevel;

    assert_eq!(
        SignificanceLevel::from_p_value(0.001),
        SignificanceLevel::HighlySignificant
    );
    assert_eq!(
        SignificanceLevel::from_p_value(0.02),
        SignificanceLevel::Significant
    );
    assert_eq!(
        SignificanceLevel::from_p_value(0.07),
        SignificanceLevel::Marginal
    );
    assert_eq!(
        SignificanceLevel::from_p_value(0.5),
        SignificanceLevel::NotSignificant
    );
}

#[test]
fn test_proportion_stats() {
    use cardgame::diagnostics::{ProportionStats, SignificanceLevel};

    let stats = ProportionStats::calculate(65, 100, 0.5);

    assert!((stats.proportion - 0.65).abs() < 0.01);
    assert!(stats.ci_lower > 0.5);
    assert!(stats.ci_upper < 0.8);
    assert!(stats.p_value < 0.05);
    assert!(
        stats.significance == SignificanceLevel::Significant
            || stats.significance == SignificanceLevel::HighlySignificant
    );
}

// ── metrics.rs (diagnostics) ─────────────────────────────────────────────────

#[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);

    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));

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

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

#[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));

    let tempo2 = TempoMetrics {
        p1_first_creature_turn: Some(3),
        p2_first_creature_turn: Some(2),
    };
    assert_eq!(tempo2.first_creature_player(), Some(false));
}

#[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);

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

    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);
    assert!(game_metrics.turns_p1_ahead > 0);
}
