//! Tests for batch execution.

use entropyk_cli::batch::{discover_config_files, BatchAggregator, BatchSummary, OutputFormat};
use entropyk_cli::run::{SimulationResult, SimulationStatus};
use std::path::PathBuf;
use std::str::FromStr;
use tempfile::tempdir;

#[test]
fn test_discover_config_files() {
    let dir = tempdir().unwrap();

    std::fs::write(dir.path().join("config1.json"), "{}").unwrap();
    std::fs::write(dir.path().join("config2.json"), "{}").unwrap();
    std::fs::write(dir.path().join("config3.json"), "{}").unwrap();
    std::fs::write(dir.path().join("readme.txt"), "").unwrap();
    std::fs::write(dir.path().join("data.csv"), "a,b,c").unwrap();

    let files = discover_config_files(dir.path()).unwrap();
    assert_eq!(files.len(), 3);

    let names: Vec<String> = files
        .iter()
        .map(|p: &PathBuf| p.file_name().unwrap().to_string_lossy().to_string())
        .collect();
    assert!(names.contains(&"config1.json".to_string()));
    assert!(names.contains(&"config2.json".to_string()));
    assert!(names.contains(&"config3.json".to_string()));
}

#[test]
fn test_discover_config_files_sorted() {
    let dir = tempdir().unwrap();

    std::fs::write(dir.path().join("zebra.json"), "{}").unwrap();
    std::fs::write(dir.path().join("alpha.json"), "{}").unwrap();
    std::fs::write(dir.path().join("middle.json"), "{}").unwrap();

    let files = discover_config_files(dir.path()).unwrap();
    assert_eq!(files.len(), 3);
    assert!(files[0].ends_with("alpha.json"));
    assert!(files[1].ends_with("middle.json"));
    assert!(files[2].ends_with("zebra.json"));
}

#[test]
fn test_discover_empty_directory() {
    let dir = tempdir().unwrap();
    let files = discover_config_files(dir.path()).unwrap();
    assert!(files.is_empty());
}

#[test]
fn test_batch_summary_serialization() {
    let summary = BatchSummary {
        total: 100,
        succeeded: 95,
        failed: 3,
        non_converged: 2,
        total_elapsed_ms: 5000,
        avg_elapsed_ms: 50.0,
        results: vec![],
    };

    let json = serde_json::to_string_pretty(&summary).unwrap();
    assert!(json.contains("\"total\": 100"));
    assert!(json.contains("\"succeeded\": 95"));
    assert!(json.contains("\"avg_elapsed_ms\": 50.0"));
}

#[test]
fn test_batch_summary_default() {
    let summary = BatchSummary::default();
    assert_eq!(summary.total, 0);
    assert_eq!(summary.succeeded, 0);
    assert_eq!(summary.failed, 0);
    assert!(summary.results.is_empty());
}

#[test]
fn test_simulation_result_statuses() {
    let results = vec![
        SimulationResult {
            input: "ok.json".to_string(),
            status: SimulationStatus::Converged,
            convergence: None,
            iterations: Some(10),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 50,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "fail.json".to_string(),
            status: SimulationStatus::Error,
            convergence: None,
            iterations: None,
            state: None,
            performance: None,
            error: Some("Error".to_string()),
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 0,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "timeout.json".to_string(),
            status: SimulationStatus::Timeout,
            convergence: None,
            iterations: Some(100),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 1000,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
    ];

    let converged_count = results
        .iter()
        .filter(|r| r.status == SimulationStatus::Converged)
        .count();
    let error_count = results
        .iter()
        .filter(|r| r.status == SimulationStatus::Error)
        .count();
    let timeout_count = results
        .iter()
        .filter(|r| r.status == SimulationStatus::Timeout)
        .count();

    assert_eq!(converged_count, 1);
    assert_eq!(error_count, 1);
    assert_eq!(timeout_count, 1);
}

#[test]
fn test_batch_aggregator_csv_output() {
    let results = vec![
        SimulationResult {
            input: "scenario1.json".to_string(),
            status: SimulationStatus::Converged,
            convergence: Some(entropyk_cli::run::ConvergenceInfo {
                final_residual: 1e-8,
                tolerance: 1e-6,
                iterations: None,
                strategy: None,
                iteration_history: vec![],
            }),
            iterations: Some(25),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 150,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "scenario2.json".to_string(),
            status: SimulationStatus::Converged,
            convergence: Some(entropyk_cli::run::ConvergenceInfo {
                final_residual: 5e-7,
                tolerance: 1e-6,
                iterations: None,
                strategy: None,
                iteration_history: vec![],
            }),
            iterations: Some(30),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 200,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "scenario3.json".to_string(),
            status: SimulationStatus::Error,
            convergence: None,
            iterations: None,
            state: None,
            performance: None,
            error: Some("Solver failed".to_string()),
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 0,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
    ];

    let aggregator = BatchAggregator::new(results);
    let csv = aggregator.to_csv();
    let lines: Vec<&str> = csv.lines().collect();

    // Header + 3 data lines
    assert_eq!(lines.len(), 4);
    assert!(lines[0].contains("input,status,iterations,converged"));
    assert!(lines[1].contains("scenario1.json"));
    assert!(lines[1].contains("converged"));
    assert!(lines[1].contains("true"));
    assert!(lines[3].contains("error"));
}

#[test]
fn test_batch_aggregator_json_summary() {
    let results = vec![
        SimulationResult {
            input: "test1.json".to_string(),
            status: SimulationStatus::Converged,
            convergence: None,
            iterations: Some(10),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 50,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "test2.json".to_string(),
            status: SimulationStatus::Converged,
            convergence: None,
            iterations: Some(15),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 75,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
        SimulationResult {
            input: "test3.json".to_string(),
            status: SimulationStatus::Timeout,
            convergence: None,
            iterations: Some(100),
            state: None,
            performance: None,
            error: None,
            failure_diagnostics: None,
            initialization_diagnostics: None,
            dof: None,
            elapsed_ms: 5000,
            raw_state_vector: None,
            solved_variables: Vec::new(),
        },
    ];

    let aggregator = BatchAggregator::new(results);
    let summary = aggregator.summary();

    assert_eq!(summary.total, 3);
    assert_eq!(summary.succeeded, 2);
    assert_eq!(summary.failed, 0);
    assert_eq!(summary.non_converged, 1);
}

#[test]
fn test_output_format_parsing() {
    assert_eq!(OutputFormat::from_str("json").unwrap(), OutputFormat::Json);
    assert_eq!(OutputFormat::from_str("JSON").unwrap(), OutputFormat::Json);
    assert_eq!(OutputFormat::from_str("csv").unwrap(), OutputFormat::Csv);
    assert_eq!(OutputFormat::from_str("CSV").unwrap(), OutputFormat::Csv);
    assert!(OutputFormat::from_str("xml").is_err());
    assert!(OutputFormat::from_str("invalid").is_err());
}

#[test]
fn test_batch_summary_json_serialization_roundtrip() {
    let summary = BatchSummary {
        total: 20,
        succeeded: 18,
        failed: 1,
        non_converged: 1,
        total_elapsed_ms: 5000,
        avg_elapsed_ms: 250.0,
        results: vec![],
    };

    let json = summary.to_json().unwrap();
    let deserialized: BatchSummary = serde_json::from_str(&json).unwrap();

    assert_eq!(deserialized.total, 20);
    assert_eq!(deserialized.succeeded, 18);
    assert_eq!(deserialized.failed, 1);
    assert_eq!(deserialized.non_converged, 1);
    assert_eq!(deserialized.total_elapsed_ms, 5000);
}

#[test]
fn test_batch_summary_csv_with_convergence() {
    let results = vec![SimulationResult {
        input: "config1.json".to_string(),
        status: SimulationStatus::Converged,
        convergence: Some(entropyk_cli::run::ConvergenceInfo {
            final_residual: 1e-9,
            tolerance: 1e-6,
            iterations: None,
            strategy: None,
            iteration_history: vec![],
        }),
        iterations: Some(42),
        state: None,
        performance: None,
        error: None,
        failure_diagnostics: None,
        initialization_diagnostics: None,
        dof: None,
        elapsed_ms: 300,
        raw_state_vector: None,
        solved_variables: Vec::new(),
    }];

    let summary = BatchSummary {
        total: 1,
        succeeded: 1,
        failed: 0,
        non_converged: 0,
        total_elapsed_ms: 300,
        avg_elapsed_ms: 300.0,
        results,
    };

    let csv = summary.to_csv();
    assert!(csv.contains("config1.json"));
    assert!(csv.contains("converged"));
    assert!(csv.contains("42"));
    assert!(csv.contains("1.00e-9"));
    assert!(csv.contains("300"));
}
