//! Entropyk CLI - Batch thermodynamic simulation tool.
//!
//! A command-line interface for running thermodynamic simulations
//! in single or batch mode.
//!
//! # Usage
//!
//! ```text
//! entropyk-cli run config.json -o result.json
//! entropyk-cli batch ./scenarios/ --parallel 4
//! ```

use std::path::PathBuf;

use clap::{Parser, Subcommand};
use colored::Colorize;
use tracing::Level;
use tracing_subscriber::EnvFilter;

use entropyk_cli::error::{CliError, ExitCode};

#[derive(Parser)]
#[command(name = "entropyk-cli")]
#[command(author)]
#[command(version)]
#[command(about = "Batch thermodynamic simulation CLI", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,

    /// Enable verbose output
    #[arg(short, long, global = true)]
    verbose: bool,

    /// Suppress all output except errors
    #[arg(short, long, global = true, conflicts_with = "verbose")]
    quiet: bool,
}

#[derive(Subcommand)]
enum Commands {
    /// Run a single simulation from a configuration file
    Run {
        /// Path to the JSON configuration file
        #[arg(short, long, value_name = "FILE")]
        config: PathBuf,

        /// Path to write the JSON output (default: stdout)
        #[arg(short, long, value_name = "FILE")]
        output: Option<PathBuf>,
    },

    /// Run multiple simulations from a directory
    Batch {
        /// Directory containing JSON configuration files
        #[arg(short, long, value_name = "DIR")]
        directory: PathBuf,

        /// Directory to write output files
        #[arg(short, long, value_name = "DIR")]
        output_dir: Option<PathBuf>,

        /// Number of parallel workers
        #[arg(short, long, default_value = "4")]
        parallel: usize,
    },

    /// Validate a configuration file without running
    Validate {
        /// Path to the JSON configuration file
        #[arg(short, long, value_name = "FILE")]
        config: PathBuf,
    },
}

fn main() {
    let cli = Cli::parse();

    let log_level = if cli.verbose {
        Level::DEBUG
    } else if cli.quiet {
        Level::ERROR
    } else {
        Level::INFO
    };

    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::builder()
                .with_default_directive(log_level.into())
                .from_env_lossy(),
        )
        .with_target(false)
        .init();

    let result = match cli.command {
        Commands::Run { config, output } => run_single(config, output, cli.verbose, cli.quiet),
        Commands::Batch {
            directory,
            output_dir,
            parallel,
        } => run_batch(directory, output_dir, parallel, cli.quiet, cli.verbose),
        Commands::Validate { config } => validate_config(config),
    };

    match result {
        Ok(()) => std::process::exit(ExitCode::Success as i32),
        Err(e) => {
            if !cli.quiet {
                eprintln!("{} {}", "Error:".red(), e);
            }
            std::process::exit(e.exit_code() as i32);
        }
    }
}

fn run_single(
    config: PathBuf,
    output: Option<PathBuf>,
    verbose: bool,
    quiet: bool,
) -> Result<(), CliError> {
    use entropyk_cli::run::run_simulation;

    if !quiet {
        println!("{}", "═".repeat(60).cyan());
        println!("{}", "  ENTROPYK CLI - Single Simulation".cyan().bold());
        println!("{}", "═".repeat(60).cyan());
        println!();
    }

    let result = run_simulation(&config, output.as_deref(), verbose)?;

    if !quiet {
        print_result(&result);
    } else if output.is_none() {
        let json = serde_json::to_string(&result)
            .map_err(|e| CliError::Simulation(format!("Failed to serialize result: {}", e)))?;
        println!("{}", json);
    }

    match result.status {
        entropyk_cli::run::SimulationStatus::Converged => Ok(()),
        entropyk_cli::run::SimulationStatus::Timeout
        | entropyk_cli::run::SimulationStatus::NonConverged => Err(CliError::Simulation(
            "Simulation did not converge".to_string(),
        )),
        entropyk_cli::run::SimulationStatus::Error => Err(CliError::Simulation(
            result.error.unwrap_or_else(|| "Unknown error".to_string()),
        )),
    }
}

fn print_result(result: &entropyk_cli::run::SimulationResult) {
    use colored::Colorize;

    println!("{}", "─".repeat(40).white());
    println!("  Input:      {}", result.input);

    let status_str = match result.status {
        entropyk_cli::run::SimulationStatus::Converged => "CONVERGED".green(),
        entropyk_cli::run::SimulationStatus::Timeout => "TIMEOUT".yellow(),
        entropyk_cli::run::SimulationStatus::NonConverged => "NON-CONVERGED".yellow(),
        entropyk_cli::run::SimulationStatus::Error => "ERROR".red(),
    };
    println!("  Status:     {}", status_str);

    if let Some(ref conv) = result.convergence {
        println!("  Residual:   {:.2e}", conv.final_residual);
    }

    if let Some(iters) = result.iterations {
        println!("  Iterations: {}", iters);
    }

    println!("  Time:       {} ms", result.elapsed_ms);

    if let Some(ref error) = result.error {
        println!();
        println!("  {} {}", "Error:".red(), error);
    }

    if let Some(ref state) = result.state {
        println!();
        println!("  {}", "Edge States:".cyan());
        for entry in state {
            println!(
                "    Edge {}: P = {:.3} bar, h = {:.2} kJ/kg",
                entry.edge, entry.pressure_bar, entry.enthalpy_kj_kg
            );
        }
    }

    println!("{}", "─".repeat(40).white());
}

fn run_batch(
    directory: PathBuf,
    output_dir: Option<PathBuf>,
    parallel: usize,
    quiet: bool,
    verbose: bool,
) -> Result<(), CliError> {
    use entropyk_cli::batch::run_batch;

    if !quiet {
        println!("{}", "═".repeat(60).cyan());
        println!("{}", "  ENTROPYK CLI - Batch Execution".cyan().bold());
        println!("{}", "═".repeat(60).cyan());
        println!();
    }

    let summary = run_batch(&directory, parallel, output_dir.as_deref(), quiet, verbose)?;

    if summary.failed > 0 || summary.non_converged > 0 {
        Err(CliError::Simulation(format!(
            "{} simulations failed, {} non-converged",
            summary.failed, summary.non_converged
        )))
    } else {
        Ok(())
    }
}

fn validate_config(config: PathBuf) -> Result<(), CliError> {
    use entropyk_cli::config::ScenarioConfig;

    println!("{}", "═".repeat(60).cyan());
    println!(
        "{}",
        "  ENTROPYK CLI - Configuration Validation".cyan().bold()
    );
    println!("{}", "═".repeat(60).cyan());
    println!();

    let _cfg = ScenarioConfig::from_file(&config)?;

    println!("  {} Configuration is valid", "✓".green());
    println!("  File: {}", config.display());

    Ok(())
}
