//! Tests for configuration parsing.

use entropyk_cli::config::{ComponentConfig, ScenarioConfig, SolverConfig};
use entropyk_cli::error::CliError;
use std::path::PathBuf;
use tempfile::tempdir;

#[test]
fn test_parse_minimal_config() {
    let json = r#"{ "fluid": "R134a" }"#;
    let config = ScenarioConfig::from_json(json).unwrap();
    assert_eq!(config.fluid, "R134a");
    assert!(config.circuits.is_empty());
    assert_eq!(config.solver.strategy, "newton");
    assert!(config.controls.is_empty());
}

#[test]
fn test_parse_saturated_control_loop() {
    let json = r#"
    {
        "fluid": "R134a",
        "controls": [
            {
                "type": "SaturatedController",
                "id": "lwt_capacity",
                "measure": { "component": "evap_0", "output": "capacity" },
                "actuator": {
                    "component": "screw_0", "factor": "f_m",
                    "initial": 1.0, "min": 0.5, "max": 1.5
                },
                "target": 350000.0,
                "gain": 0.01,
                "band": 1.0,
                "smooth_eps": 0.001
            }
        ]
    }"#;
    let config = ScenarioConfig::from_json(json).unwrap();
    assert_eq!(config.controls.len(), 1);
    let c = &config.controls[0];
    assert_eq!(c.control_type, "SaturatedController");
    assert_eq!(c.id, "lwt_capacity");
    assert_eq!(c.measure.component, "evap_0");
    assert_eq!(c.measure.output, "capacity");
    assert_eq!(c.actuator.factor, "f_m");
    assert_eq!(c.actuator.min, 0.5);
    assert_eq!(c.actuator.max, 1.5);
    assert_eq!(c.target, 350000.0);
    assert_eq!(c.gain, 0.01);
    assert_eq!(c.smooth_eps, Some(0.001));
}

#[test]
fn test_control_defaults_apply() {
    let json = r#"
    {
        "fluid": "R134a",
        "controls": [
            {
                "id": "cap",
                "measure": { "component": "evap", "output": "capacity" },
                "actuator": { "component": "comp", "factor": "f_m", "min": 0.5, "max": 1.5 },
                "target": 1000.0
            }
        ]
    }"#;
    let config = ScenarioConfig::from_json(json).unwrap();
    let c = &config.controls[0];
    assert_eq!(c.control_type, "SaturatedController");
    assert_eq!(c.gain, 1.0);
    assert_eq!(c.band, 1.0);
    assert_eq!(c.actuator.initial, 1.0);
    assert_eq!(c.smooth_eps, None);
}

#[test]
fn test_parse_full_config() {
    let json = r#"
    {
        "fluid": "R410A",
        "circuits": [{
            "id": 0,
            "components": [
                { "type": "Condenser", "name": "cond1", "ua": 5000.0 },
                { "type": "Evaporator", "name": "evap1", "ua": 4000.0 }
            ],
            "edges": [
                { "from": "cond1:outlet", "to": "evap1:inlet" }
            ]
        }],
        "solver": {
            "strategy": "newton",
            "max_iterations": 50,
            "tolerance": 1e-8
        }
    }"#;

    let config = ScenarioConfig::from_json(json).unwrap();
    assert_eq!(config.fluid, "R410A");
    assert_eq!(config.circuits.len(), 1);
    assert_eq!(config.circuits[0].components.len(), 2);
    assert_eq!(config.solver.strategy, "newton");
    assert_eq!(config.solver.max_iterations, 50);
}

#[test]
fn test_validate_missing_fluid() {
    let json = r#"{ "fluid": "" }"#;
    let result = ScenarioConfig::from_json(json);
    assert!(result.is_err());
    if let Err(CliError::Config(msg)) = result {
        assert!(msg.contains("fluid"));
    }
}

#[test]
fn test_validate_empty_circuit() {
    let json = r#"
    {
        "fluid": "R134a",
        "circuits": [{
            "id": 0,
            "components": []
        }]
    }"#;
    let result = ScenarioConfig::from_json(json);
    assert!(result.is_err());
}

#[test]
fn test_validate_invalid_edge_format() {
    let json = r#"
    {
        "fluid": "R134a",
        "circuits": [{
            "id": 0,
            "components": [{ "type": "Condenser", "name": "cond1", "ua": 5000.0 }],
            "edges": [{ "from": "invalid", "to": "also_invalid" }]
        }]
    }"#;
    let result = ScenarioConfig::from_json(json);
    assert!(result.is_err());
    if let Err(CliError::Config(msg)) = result {
        assert!(msg.contains("edge format"));
    }
}

#[test]
fn test_load_config_from_file() {
    let dir = tempdir().unwrap();
    let config_path = dir.path().join("test.json");

    let json = r#"{ "fluid": "R744" }"#;
    std::fs::write(&config_path, json).unwrap();

    let config = ScenarioConfig::from_file(&config_path).unwrap();
    assert_eq!(config.fluid, "R744");
}

#[test]
fn test_load_config_file_not_found() {
    let result = ScenarioConfig::from_file(PathBuf::from("/nonexistent/path.json").as_path());
    assert!(result.is_err());
    if let Err(CliError::ConfigNotFound(path)) = result {
        assert!(path.to_str().unwrap().contains("nonexistent"));
    }
}

#[test]
fn test_solver_config_defaults() {
    let config = SolverConfig::default();
    assert_eq!(config.strategy, "newton");
    assert_eq!(config.max_iterations, 100);
    assert_eq!(config.tolerance, 1e-6);
    assert_eq!(config.timeout_ms, 0);
    assert!(!config.verbose);
}

#[test]
fn test_component_config_params() {
    let json = r#"
    {
        "type": "Evaporator",
        "name": "evap1",
        "ua": 4000.0,
        "t_sat_k": 278.15,
        "superheat_k": 5.0
    }"#;

    let component: ComponentConfig = serde_json::from_str(json).unwrap();
    assert_eq!(component.component_type, "Evaporator");
    assert_eq!(component.name, "evap1");
    assert_eq!(
        component.params.get("ua").unwrap().as_f64().unwrap(),
        4000.0
    );
    assert_eq!(
        component.params.get("t_sat_k").unwrap().as_f64().unwrap(),
        278.15
    );
    assert_eq!(
        component
            .params
            .get("superheat_k")
            .unwrap()
            .as_f64()
            .unwrap(),
        5.0
    );
}

#[test]
fn test_validate_edge_unknown_component() {
    let json = r#"
    {
        "fluid": "R134a",
        "circuits": [{
            "id": 0,
            "components": [{ "type": "Condenser", "name": "cond1", "ua": 5000.0 }],
            "edges": [{ "from": "cond1:outlet", "to": "nonexistent:inlet" }]
        }]
    }"#;
    let result = ScenarioConfig::from_json(json);
    assert!(result.is_err());
    if let Err(CliError::Config(msg)) = result {
        assert!(msg.contains("unknown component"));
        assert!(msg.contains("nonexistent"));
    }
}
