//! Quick Balance Validation CLI - Fast sanity check for game balance.
//!
//! Tests all deck combinations across faction pairs using Greedy bot for speed.
//! Designed for quick validation after card changes, CI pipelines, and sanity checks.
//!
//! For thorough statistical analysis with stronger bots, use `benchmark` instead.
//!
//! Usage:
//!   cargo run --release --bin validate                         # Quick check (~1 sec)
//!   cargo run --release --bin validate -- --games-per-matchup 50  # More games for confidence
//!   cargo run --release --bin validate -- --progress           # Show progress

use std::path::PathBuf;
use std::process;
use std::time::Instant;

use clap::Parser;

use cardgame::bots::BotType;
use cardgame::execution::{configure_thread_pool, GameData, MatchupBuilder};
use cardgame::validation::{
    export_json, export_matrix_csv, find_outliers, print_matchup_matrix, print_results,
    run_outlier_diagnostics, save_validation_results, ArchetypeWeights, AutoDiagnoseConfig,
    BalanceAnalyzer, ValidationConfig, ValidationExecutor, ValidationResults,
};
use cardgame::version::{self, VersionInfo};

/// Quick Balance Validation - Fast sanity check with Greedy bot
#[derive(Parser, Debug)]
#[command(name = "validate")]
#[command(about = "Quick balance validation using Greedy bot (~1 sec for default settings)", long_about = None)]
struct Args {
    /// Games per matchup per player order (total = matchups × 2 × games)
    #[arg(long, short = 'n', default_value = "10")]
    games_per_matchup: usize,

    /// Output JSON file path (legacy, prefer --run-id for full output)
    #[arg(long, short = 'o')]
    output: Option<PathBuf>,

    /// Output directory for validation results (creates experiments/validation/{run_id}/)
    /// If not specified, uses timestamp: YYYY-MM-DD_HHMM
    #[arg(long)]
    run_id: Option<String>,

    /// Show progress indicator
    #[arg(long)]
    progress: bool,

    /// Random seed for reproducibility
    #[arg(long, short = 's', default_value = "42")]
    seed: u64,

    /// Number of threads (0 = use all cores)
    #[arg(long, short = 'j', default_value = "0")]
    threads: usize,

    /// Path to card database
    #[arg(long, default_value = "data/cards/core_set")]
    cards: PathBuf,

    /// Path to commanders directory
    #[arg(long, default_value = "data/commanders")]
    commanders: PathBuf,

    /// Path to deck definitions directory
    #[arg(long, default_value = "data/decks")]
    decks: PathBuf,

    /// Path to weights directory
    #[arg(long, default_value = "data/weights")]
    weights: PathBuf,

    /// Run only a specific matchup (e.g., "argentum-symbiote", "argentum-obsidion", "symbiote-obsidion")
    #[arg(long, short = 'm')]
    matchup: Option<String>,

    /// Print matchup matrix after results
    #[arg(long)]
    matrix: bool,

    /// Export matchup matrix to CSV file
    #[arg(long)]
    matrix_csv: Option<PathBuf>,

    /// Auto-diagnose outlier decks after validation
    #[arg(long)]
    auto_diagnose: bool,

    /// Win rate delta from 50% to trigger diagnosis (default: 0.10 = 40-60% range)
    #[arg(long, default_value = "0.10")]
    outlier_threshold: f64,

    /// Number of games for each diagnostic run
    #[arg(long, default_value = "50")]
    diagnose_games: usize,
}

fn main() {
    env_logger::init();
    let args = Args::parse();

    // Greedy bot is hardcoded for fast validation
    let bot_type = BotType::Greedy;

    // Configure thread pool
    let num_threads = configure_thread_pool(args.threads);

    // Load game data using unified loader
    let game_data = match GameData::load_with_overrides(
        Some(&args.cards),
        Some(&args.commanders),
        Some(&args.decks),
        Some(&args.weights),
        !args.progress,
    ) {
        Ok(data) => data,
        Err(e) => {
            eprintln!("Error loading game data: {}", e);
            process::exit(1);
        }
    };

    // Load archetype weights for bots
    let archetype_weights = ArchetypeWeights::load_from_directory(&args.weights, !args.progress);

    // Build matchups using new MatchupBuilder (preserves commander info)
    let builder = MatchupBuilder::new(&game_data.deck_registry);
    let mut matchups = builder.build_inter_faction_matchups();

    if matchups.is_empty() {
        eprintln!("Error: No valid faction matchups found. Need decks for at least 2 factions.");
        process::exit(1);
    }

    // Filter to specific matchup if requested
    if let Some(ref matchup_filter) = args.matchup {
        matchups = MatchupBuilder::filter_matchups(matchups, matchup_filter);
        if matchups.is_empty() {
            eprintln!(
                "Error: No matchup found matching '{}'. Valid options: argentum-symbiote, argentum-obsidion, symbiote-obsidion",
                matchup_filter
            );
            process::exit(1);
        }
    }

    // Print header
    println!("=== Quick Balance Validation ===");
    println!("Version: {}", version::version_string());
    println!(
        "Config: {} games/matchup, Greedy bot, {} threads",
        args.games_per_matchup, num_threads
    );
    println!("Matchups: {} deck pairs (round-robin)", matchups.len());
    println!(
        "Total games: {} (matchups × 2 directions × {})",
        matchups.len() * 2 * args.games_per_matchup,
        args.games_per_matchup
    );
    println!();

    // Run validation with greedy bot (no MCTS sims needed, but API requires it)
    let start_time = Instant::now();
    let executor = ValidationExecutor::new(&game_data.card_db, 0)
        .with_bot_type(bot_type.clone())
        .with_progress(args.progress);

    let matchup_results = match executor.run_all(
        &matchups,
        &archetype_weights,
        args.games_per_matchup,
        args.seed,
    ) {
        Ok(results) => results,
        Err(e) => {
            eprintln!("Error: {}", e);
            process::exit(1);
        }
    };
    let total_time = start_time.elapsed();

    // Analyze balance
    let analyzer = BalanceAnalyzer::new();
    let summary = analyzer.analyze(&matchup_results);

    // Create full results
    let config = ValidationConfig::new(args.games_per_matchup, 0, args.seed, num_threads)
        .with_matchup_filter(args.matchup.clone());

    let results = ValidationResults {
        timestamp: chrono::Utc::now().to_rfc3339(),
        version: VersionInfo::current(),
        config,
        matchups: matchup_results,
        summary,
    };

    // Output results
    print_results(&results, total_time);

    // Print matchup matrix if requested
    if args.matrix {
        print_matchup_matrix(&results.matchups);
    }

    // Export matrix to CSV if requested
    if let Some(ref csv_path) = args.matrix_csv {
        if let Err(e) = export_matrix_csv(&results.matchups, csv_path) {
            eprintln!("Error exporting matrix CSV: {}", e);
            process::exit(1);
        }
        println!("Matrix exported to {:?}", csv_path);
    }

    // Save to timestamped directory by default
    let output_dir = if let Some(ref output_path) = args.output {
        // Legacy mode: save only JSON to specified path
        if let Err(e) = export_json(&results, output_path) {
            eprintln!("Error: {}", e);
            process::exit(1);
        }
        None
    } else {
        // New mode: save full results to timestamped directory
        match save_validation_results(&results, total_time, args.run_id.as_deref()) {
            Ok(dir) => Some(dir),
            Err(e) => {
                eprintln!("Error saving results: {}", e);
                process::exit(1);
            }
        }
    };

    // Auto-diagnose outlier decks if requested
    if args.auto_diagnose {
        let outliers = find_outliers(
            &results.summary,
            &game_data.deck_registry,
            args.outlier_threshold,
        );

        if outliers.is_empty() {
            println!(
                "\nNo outlier decks found (all within {:.0}-{:.0}% win rate range).",
                (0.5 - args.outlier_threshold) * 100.0,
                (0.5 + args.outlier_threshold) * 100.0
            );
        } else {
            println!(
                "\n=== Auto-Diagnosing {} Outlier Deck(s) ===",
                outliers.len()
            );

            let diagnose_config = AutoDiagnoseConfig {
                games: args.diagnose_games,
                bot_type: bot_type.clone(),
                alphabeta_depth: 6, // Not used for greedy, but required by struct
                mcts_sims: 0,
                seed: args.seed,
            };

            // Determine output directory for diagnostics
            let diag_output_dir = output_dir.unwrap_or_else(|| {
                args.output
                    .as_ref()
                    .and_then(|p| p.parent())
                    .map(|p| p.to_path_buf())
                    .unwrap_or_else(|| PathBuf::from("experiments/validation/diagnostics"))
            });

            if let Err(e) = run_outlier_diagnostics(
                &outliers,
                &game_data.deck_registry,
                &game_data.card_db,
                &diagnose_config,
                &diag_output_dir,
                args.progress,
            ) {
                eprintln!("Error running auto-diagnostics: {}", e);
                process::exit(1);
            }
        }
    }
}
