//! Epic 1 Integration Tests - Extensible Component Framework
//!
//! Tests for User Stories:
//! - 1-1: Component Trait Definition
//! - 1-2: Physical Types (NewType Pattern)
//! - 1-3: Port and Connection System
//! - 1-4: Compressor Component (AHRI 540)
//! - 1-5: Generic Heat Exchanger Framework
//! - 1-6: Expansion Valve Component
//! - 1-7: Component State Machine (ON/OFF/BYPASS)
//! - 1-8: Auxiliary and Transport Components
//! - 1-11: Flow Junctions (Splitter/Merger)
//! - 1-12: Boundary Conditions (Source/Sink)

use approx::assert_relative_eq;
use entropyk_components::{
    Ahri540Coefficients, Compressor, CompressorModel, ConnectedPort, EpsNtuModel, ExchangerType,
    ExpansionValve, FlowSink, FlowSource, FlowSplitter, FluidId, HeatExchanger, OperationalState,
    Pipe, Port, Pump, SstSdtCoefficients, StateManageable,
};
use entropyk_core::{Enthalpy, MassFlow, Pressure, Temperature};

// =============================================================================
// Story 1-2: Physical Types (NewType Pattern)
// =============================================================================

mod story_1_2_types {
    use super::*;

    #[test]
    fn test_pressure_conversions() {
        let p_bar = Pressure::from_bar(1.0);
        assert_relative_eq!(p_bar.to_pascals(), 100_000.0, epsilon = 1e-6);
        assert_relative_eq!(p_bar.to_bar(), 1.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_conversions() {
        let t_c = Temperature::from_celsius(0.0);
        assert_relative_eq!(t_c.to_kelvin(), 273.15, epsilon = 1e-10);
        assert_relative_eq!(t_c.to_celsius(), 0.0, epsilon = 1e-10);
    }

    #[test]
    fn test_enthalpy_conversions() {
        let h_kj = Enthalpy::from_kilojoules_per_kg(100.0);
        assert_relative_eq!(h_kj.to_joules_per_kg(), 100_000.0, epsilon = 1e-6);
    }

    #[test]
    fn test_mass_flow_regularization() {
        let zero = MassFlow::from_kg_per_s(0.0);
        let regularized = zero.regularized();
        assert!(regularized.to_kg_per_s() > 0.0);
    }
}

// =============================================================================
// Story 1-3: Port and Connection System
// =============================================================================

mod story_1_3_ports {
    use super::*;

    #[test]
    fn test_port_creation() {
        let port = Port::new(
            FluidId::new("R134a"),
            Pressure::from_bar(10.0),
            Enthalpy::from_joules_per_kg(400_000.0),
        );
        assert_eq!(port.fluid_id().as_str(), "R134a");
    }

    #[test]
    fn test_port_connection_success() {
        let p1 = Port::new(
            FluidId::new("R134a"),
            Pressure::from_bar(10.0),
            Enthalpy::from_joules_per_kg(400_000.0),
        );
        let p2 = Port::new(
            FluidId::new("R134a"),
            Pressure::from_bar(10.0),
            Enthalpy::from_joules_per_kg(400_000.0),
        );

        let result = p1.connect(p2);
        assert!(result.is_ok());
    }

    #[test]
    fn test_port_connection_fluid_mismatch() {
        let p1 = Port::new(
            FluidId::new("R134a"),
            Pressure::from_bar(10.0),
            Enthalpy::from_joules_per_kg(400_000.0),
        );
        let p2 = Port::new(
            FluidId::new("R410A"),
            Pressure::from_bar(10.0),
            Enthalpy::from_joules_per_kg(400_000.0),
        );

        let result = p1.connect(p2);
        assert!(result.is_err());
    }
}

// =============================================================================
// Story 1-4: Compressor Component (AHRI 540)
// =============================================================================

mod story_1_4_compressor {
    use super::*;

    #[test]
    fn test_ahri540_coefficients_creation() {
        let coeffs = Ahri540Coefficients::new(
            0.85, 2.5, // M1, M2 (flow)
            500.0, 1500.0, -2.5, 1.8, // M3-M6 (cooling)
            600.0, 1600.0, -3.0, 2.0, // M7-M10 (heating)
        );

        assert!(coeffs.validate().is_ok());
    }

    #[test]
    fn test_ahri540_invalid_m2() {
        let coeffs = Ahri540Coefficients::new(
            0.85, -1.0, // M2 must be positive
            500.0, 1500.0, -2.5, 1.8, 600.0, 1600.0, -3.0, 2.0,
        );

        assert!(coeffs.validate().is_err());
    }

    #[test]
    fn test_sst_sdt_coefficients() {
        let coeffs = SstSdtCoefficients::default();
        // Verify the polynomial structure is correct
        assert_eq!(coeffs.mass_flow.len(), 16); // 4x4 matrix
        assert_eq!(coeffs.power.len(), 16);
    }
}

// =============================================================================
// Story 1-5: Generic Heat Exchanger Framework
// =============================================================================

mod story_1_5_heat_exchanger {
    use super::*;

    #[test]
    fn test_eps_ntu_counter_flow() {
        let model = EpsNtuModel::counter_flow(5000.0);
        assert_eq!(model.ua(), 5000.0);
    }

    #[test]
    fn test_eps_ntu_effectiveness_counter_flow() {
        let model = EpsNtuModel::counter_flow(5000.0);

        // Test effectiveness calculation
        let ntu = 1.0;
        let c_r = 0.5;
        let eps = model.effectiveness(ntu, c_r);

        // For counter-flow with NTU=1, C_r=0.5: ε ≈ 0.63
        assert!(eps > 0.0 && eps < 1.0);
    }

    #[test]
    fn test_eps_ntu_phase_change() {
        let model = EpsNtuModel::counter_flow(5000.0);

        // Phase change: C_r → 0
        let ntu = 2.0;
        let c_r = 1e-12; // Effectively zero
        let eps = model.effectiveness(ntu, c_r);

        // For phase change: ε = 1 - exp(-NTU)
        let expected = 1.0 - (-ntu).exp();
        assert_relative_eq!(eps, expected, epsilon = 1e-6);
    }
}

// =============================================================================
// Story 1-6: Expansion Valve Component
// =============================================================================

mod story_1_6_expansion_valve {
    use super::*;

    #[test]
    fn test_valve_creation() {
        let inlet = Port::new(
            FluidId::new("R410A"),
            Pressure::from_bar(25.0),
            Enthalpy::from_joules_per_kg(250_000.0),
        );
        let outlet = Port::new(
            FluidId::new("R410A"),
            Pressure::from_bar(25.0),
            Enthalpy::from_joules_per_kg(250_000.0),
        );

        let valve = ExpansionValve::new(inlet, outlet, Some(1.0));
        assert!(valve.is_ok());
    }

    #[test]
    fn test_valve_invalid_opening() {
        let inlet = Port::new(
            FluidId::new("R410A"),
            Pressure::from_bar(25.0),
            Enthalpy::from_joules_per_kg(250_000.0),
        );
        let outlet = Port::new(
            FluidId::new("R410A"),
            Pressure::from_bar(25.0),
            Enthalpy::from_joules_per_kg(250_000.0),
        );

        // Opening > 1.0 should fail
        let valve = ExpansionValve::new(inlet, outlet, Some(1.5));
        assert!(valve.is_err());
    }
}

// =============================================================================
// Story 1-7: Component State Machine
// =============================================================================

mod story_1_7_state_machine {
    use super::*;

    #[test]
    fn test_operational_state_transitions() {
        let on = OperationalState::On;
        let off = OperationalState::Off;
        let bypass = OperationalState::Bypass;

        // On can transition to any state
        assert!(on.can_transition_to(OperationalState::Off));
        assert!(on.can_transition_to(OperationalState::Bypass));

        // Off can transition to On
        assert!(off.can_transition_to(OperationalState::On));
    }

    #[test]
    fn test_circuit_id_creation() {
        let circuit = entropyk_components::CircuitId::from_number(5);
        assert_eq!(circuit.as_number(), 5);
    }
}

// =============================================================================
// Story 1-8: Auxiliary Components (Pipe, Pump)
// =============================================================================

mod story_1_8_auxiliary {
    use super::*;

    #[test]
    fn test_pipe_creation() {
        use entropyk_components::PipeGeometry;

        let geometry = PipeGeometry {
            length_m: 10.0,
            diameter_m: 0.022,
            roughness_m: 1.5e-6,
        };

        assert_relative_eq!(geometry.length_m, 10.0);
        assert_relative_eq!(geometry.diameter_m, 0.022);
    }

    #[test]
    fn test_pump_curves() {
        use entropyk_components::PumpCurves;

        let curves = PumpCurves {
            h0: 30.0,
            h1: -10.0,
            h2: -50.0,
            eta0: 0.5,
            eta1: 0.3,
            eta2: -0.5,
        };

        // At Q=0, H should be H0
        let h_at_zero = curves.head(0.0);
        assert_relative_eq!(h_at_zero, 30.0, epsilon = 1e-6);
    }
}

// =============================================================================
// Story 1-11: Flow Junctions
// =============================================================================

mod story_1_11_junctions {
    use super::*;

    #[test]
    fn test_flow_splitter_creation() {
        let inlet = Port::new(
            FluidId::new("Water"),
            Pressure::from_bar(1.0),
            Enthalpy::from_joules_per_kg(42_000.0),
        );
        let outlet1 = Port::new(
            FluidId::new("Water"),
            Pressure::from_bar(1.0),
            Enthalpy::from_joules_per_kg(42_000.0),
        );
        let outlet2 = Port::new(
            FluidId::new("Water"),
            Pressure::from_bar(1.0),
            Enthalpy::from_joules_per_kg(42_000.0),
        );

        let splitter = FlowSplitter::new(inlet, vec![outlet1, outlet2]);
        assert!(splitter.is_ok());
    }

    #[test]
    fn test_flow_source_creation() {
        let source = FlowSource::new(
            FluidId::new("Water"),
            Pressure::from_bar(1.0),
            Enthalpy::from_joules_per_kg(42_000.0),
        );

        assert_eq!(source.fluid_id().as_str(), "Water");
    }

    #[test]
    fn test_flow_sink_creation() {
        let sink = FlowSink::new(FluidId::new("Water"), Pressure::from_bar(1.0));

        assert_eq!(sink.fluid_id().as_str(), "Water");
    }
}
