//! End-to-end integration test for the **emergent-pressure** refrigeration cycle.
//!
//! This test assembles the REAL thermodynamic components
//! (`IsentropicCompressor`, `Condenser`, `IsenthalpicExpansionValve`,
//! `Evaporator`) — not mocks — with a real CoolProp fluid backend and solves the
//! canonical 4-component loop with the Newton solver.
//!
//! Unlike the fixed-design-point path (where the compressor pins
//! `P_cond = P_sat(t_cond_k)` and the EXV pins `P_evap = P_sat(t_evap_k)`), every
//! component here runs in **emergent-pressure mode**:
//!
//! | Component | emergent equations | pins |
//! |-----------|--------------------|------|
//! | Compressor | ṁ = ρ_suc·V_s·N·η_vol ; h_dis(P_suc,h_suc,P_dis) | ṁ, h_dis |
//! | Condenser  | P2=P1 ; ṁ(h1−h2)=ε·C·(T_cond(P1)−T_sec,in) ; h2=h_satliq(P1)−cp·ΔT_sc | **P_cond** |
//! | EXV        | h3=h2 (isenthalpic only) | h3 |
//! | Evaporator | P4=P3 ; ṁ(h4−h3)=ε·C·(T_sec,in−T_evap(P3)) ; h4=h(P3,T_evap+SH) | **P_evap** |
//!
//! DoF (same-branch series loop): 2 + 3 + 1 + 3 = **9 equations / 9 unknowns**.
//! The condensing/evaporating pressures are therefore EMERGENT: they are
//! determined by the heat-exchanger ↔ secondary balance, not imposed by the
//! compressor/EXV design points. The test verifies that varying the secondary
//! (water) inlet temperature genuinely moves the emergent pressures and COP.
//!
//! Requires the `coolprop` feature (entropy + saturation properties), which the
//! mock `TestBackend` does not provide:
//!   cargo test -p entropyk-solver --features coolprop --test emergent_pressure_cycle
#![cfg(feature = "coolprop")]

use std::sync::Arc;

use entropyk_components::isentropic_compressor::VolumetricEfficiency;
use entropyk_components::{Condenser, Evaporator, IsenthalpicExpansionValve, IsentropicCompressor};
use entropyk_fluids::{CoolPropBackend, FluidBackend};
use entropyk_solver::solver::{NewtonConfig, Solver};
use entropyk_solver::system::System;

/// State-vector layout (CM1.4 same-branch series loop, 9 unknowns):
/// `[ṁ, P0,h0, P1,h1, P2,h2, P3,h3]` where
///   E0 comp→cond, E1 cond→exv, E2 exv→evap, E3 evap→comp.
const N_STATE: usize = 9;

/// Result of a converged emergent-pressure solve, in engineering units.
struct CycleResult {
    m_dot: f64,  // kg/s
    p_cond: f64, // Pa (emergent condensing pressure, edge E0)
    p_evap: f64, // Pa (emergent evaporating pressure, edge E3)
    w_comp: f64, // W  (compression power)
    q_evap: f64, // W  (cooling capacity)
    cop: f64,    // -  (Q_evap / W_comp)
}

/// Assembles and solves the emergent-pressure cycle for the given secondary
/// (water) inlet temperatures and returns the converged operating point.
fn solve_emergent_cycle(cond_sec_temp_k: f64, evap_sec_temp_k: f64) -> CycleResult {
    let backend: Arc<dyn FluidBackend> = Arc::new(CoolPropBackend::new());
    let fluid = "R134a";

    // ── Compressor: emergent ṁ via volumetric displacement ────────────────────
    // ṁ = ρ_suc · V_s · N · η_vol.  V_s·N ≈ 3.25e-3 m³/s ⇒ ṁ ≈ 0.05 kg/s.
    let comp = Box::new(
        IsentropicCompressor::new(0.70, 318.15, 278.15, 5.0)
            .with_refrigerant(fluid)
            .with_fluid_backend(backend.clone())
            .with_displacement(6.5e-5, 50.0, VolumetricEfficiency::Constant(0.92)),
    );

    // ── Condenser: emergent P_cond via subcooling outlet closure ──────────────
    let cond = Box::new(
        Condenser::new(766.0)
            .with_refrigerant(fluid)
            .with_fluid_backend(backend.clone())
            .with_secondary_stream(cond_sec_temp_k, 1500.0)
            .with_emergent_pressure(5.0),
    );

    // ── EXV: emergent (isenthalpic only, drops the P_evap fix) ────────────────
    let exv = Box::new(
        IsenthalpicExpansionValve::new(278.15)
            .with_refrigerant(fluid)
            .with_fluid_backend(backend.clone())
            .with_emergent_pressure(),
    );

    // ── Evaporator: emergent P_evap via superheat outlet closure ──────────────
    let evap = Box::new(
        Evaporator::new(1468.0)
            .with_refrigerant(fluid)
            .with_fluid_backend(backend.clone())
            .with_secondary_stream(evap_sec_temp_k, 2000.0)
            .with_emergent_pressure(),
    );

    let mut system = System::new();
    let n_comp = system.add_component(comp);
    let n_cond = system.add_component(cond);
    let n_exv = system.add_component(exv);
    let n_evap = system.add_component(evap);

    system.add_edge(n_comp, n_cond).unwrap(); // E0 comp→cond
    system.add_edge(n_cond, n_exv).unwrap(); // E1 cond→exv
    system.add_edge(n_exv, n_evap).unwrap(); // E2 exv→evap
    system.add_edge(n_evap, n_comp).unwrap(); // E3 evap→comp

    system.finalize().unwrap();

    // DoF must be exactly balanced (2+3+1+3 = 9 == 9 unknowns).
    assert_eq!(
        system.full_state_vector_len(),
        N_STATE,
        "emergent same-branch loop must be 1 ṁ + 4×2(P,h) = 9 unknowns"
    );

    // Physically-consistent seed near the expected operating point.
    // P_sat(R134a): 5 °C ≈ 3.50 bar, 45 °C ≈ 11.6 bar.
    let initial_state = vec![
        0.05, // ṁ  [kg/s]
        11.6e5, 445e3, // E0 comp→cond : P_cond, h_dis
        11.6e5, 262e3, // E1 cond→exv  : P_cond, h_liq
        3.50e5, 262e3, // E2 exv→evap  : P_evap, h (isenthalpic)
        3.50e5, 405e3, // E3 evap→comp : P_evap, h_suction (superheated)
    ];

    let mut config = NewtonConfig {
        max_iterations: 200,
        tolerance: 1e-6,
        line_search: true,
        use_numerical_jacobian: false,
        initial_state: Some(initial_state),
        ..NewtonConfig::default()
    };

    let converged = config
        .solve(&mut system)
        .expect("emergent-pressure cycle must converge");

    let sv = &converged.state;
    let m_dot = sv[0];
    let (p_cond, h_dis) = (sv[1], sv[2]);
    let h_cond_out = sv[4];
    let (p_evap, h_suc) = (sv[7], sv[8]);
    let h_evap_in = sv[6];
    let h_evap_out = sv[8];

    let w_comp = m_dot * (h_dis - h_suc);
    let q_evap = m_dot * (h_evap_out - h_evap_in);

    // Sanity: subcooled liquid at condenser outlet, superheated vapour at suction.
    assert!(h_dis > h_suc, "discharge enthalpy must exceed suction");
    assert!(
        h_cond_out < h_suc,
        "condenser outlet must be subcooled liquid"
    );
    assert!(w_comp > 0.0, "compression power must be positive");
    assert!(q_evap > 0.0, "cooling capacity must be positive");

    CycleResult {
        m_dot,
        p_cond,
        p_evap,
        w_comp,
        q_evap,
        cop: q_evap / w_comp,
    }
}

/// The emergent-pressure loop must converge and produce a physical operating
/// point (positive capacity, positive power, plausible pressures/COP).
#[test]
fn test_emergent_cycle_converges_to_physical_point() {
    let r = solve_emergent_cycle(303.15, 285.15); // cond water 30 °C, evap water 12 °C

    // Emergent pressures land in a physically reasonable R134a window.
    assert!(
        (5.0e5..20.0e5).contains(&r.p_cond),
        "emergent P_cond out of range: {:.0} Pa",
        r.p_cond
    );
    assert!(
        (1.5e5..6.0e5).contains(&r.p_evap),
        "emergent P_evap out of range: {:.0} Pa",
        r.p_evap
    );
    assert!(
        r.p_cond > r.p_evap,
        "condensing must exceed evaporating pressure"
    );
    assert!(r.m_dot > 0.0, "mass flow must be positive: {}", r.m_dot);
    assert!(
        (1.5..12.0).contains(&r.cop),
        "COP out of physical range: {:.2}",
        r.cop
    );
}

/// **Core emergence claim**: warming the condenser secondary (water) inlet must
/// raise the emergent condensing pressure and reduce COP — the machine
/// performance is genuinely qualified by the secondary conditions, not fixed by
/// compressor design points.
#[test]
fn test_warmer_condenser_water_raises_pcond_and_lowers_cop() {
    let cool = solve_emergent_cycle(303.15, 285.15); // 30 °C condenser water
    let warm = solve_emergent_cycle(313.15, 285.15); // 40 °C condenser water

    assert!(
        warm.p_cond > cool.p_cond + 1.0e4,
        "warmer condenser water must raise emergent P_cond: {:.0} → {:.0} Pa",
        cool.p_cond,
        warm.p_cond
    );
    assert!(
        warm.w_comp > cool.w_comp,
        "higher lift must increase compression power: {:.0} → {:.0} W",
        cool.w_comp,
        warm.w_comp
    );
    assert!(
        warm.cop < cool.cop,
        "warmer condenser water must lower COP: {:.2} → {:.2}",
        cool.cop,
        warm.cop
    );
}

/// Warming the evaporator secondary (water/brine) inlet must raise the emergent
/// evaporating pressure and increase cooling capacity.
#[test]
fn test_warmer_evaporator_water_raises_pevap_and_capacity() {
    let cold = solve_emergent_cycle(303.15, 283.15); // 10 °C evaporator water
    let warm = solve_emergent_cycle(303.15, 291.15); // 18 °C evaporator water

    assert!(
        warm.p_evap > cold.p_evap + 1.0e4,
        "warmer evaporator water must raise emergent P_evap: {:.0} → {:.0} Pa",
        cold.p_evap,
        warm.p_evap
    );
    assert!(
        warm.q_evap > cold.q_evap,
        "warmer evaporator water must increase capacity: {:.0} → {:.0} W",
        cold.q_evap,
        warm.q_evap
    );
}
