//! Condenser Component
//!
//! A heat exchanger configured for refrigerant condensation.
//! The refrigerant (hot side) condenses from superheated vapor to
//! subcooled liquid, releasing heat to the cold side.

use super::exchanger::HeatExchanger;
use super::lmtd::{FlowConfiguration, LmtdModel};
use crate::state_machine::{CircuitId, OperationalState, StateManageable};
use crate::{
    Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector, StateSlice,
};
use entropyk_core::Calib;

/// Condenser heat exchanger.
///
/// Uses the LMTD method for heat transfer calculation.
/// The refrigerant condenses on the hot side, releasing heat
/// to the cold side (typically water or air).
///
/// # Configuration
///
/// - Hot side: Refrigerant condensing (phase change)
/// - Cold side: Heat sink (water, air, etc.)
///
/// # Example
///
/// ```
/// use entropyk_components::heat_exchanger::Condenser;
/// use entropyk_components::Component;
///
/// let condenser = Condenser::new(10_000.0);  // UA = 10 kW/K
/// assert_eq!(condenser.n_equations(), 3);
/// ```
#[derive(Debug)]
pub struct Condenser {
    /// Inner heat exchanger with LMTD model
    inner: HeatExchanger<LmtdModel>,
    /// Saturation temperature for condensation (K)
    saturation_temp: f64,
}

impl Condenser {
    /// Creates a new condenser with the given UA value.
    ///
    /// # Arguments
    ///
    /// * `ua` - Overall heat transfer coefficient × Area (W/K)
    ///
    /// # Example
    ///
    /// ```
    /// use entropyk_components::heat_exchanger::Condenser;
    ///
    /// let condenser = Condenser::new(15_000.0);
    /// ```
    pub fn new(ua: f64) -> Self {
        let model = LmtdModel::new(ua, FlowConfiguration::CounterFlow);
        Self {
            inner: HeatExchanger::new(model, "Condenser"),
            saturation_temp: 323.15,
        }
    }

    /// Creates a condenser with a specific saturation temperature.
    pub fn with_saturation_temp(ua: f64, saturation_temp: f64) -> Self {
        let model = LmtdModel::new(ua, FlowConfiguration::CounterFlow);
        Self {
            inner: HeatExchanger::new(model, "Condenser"),
            saturation_temp,
        }
    }

    /// Returns the name of this condenser.
    pub fn name(&self) -> &str {
        self.inner.name()
    }

    /// Returns the UA value (effective: f_ua × UA_nominal).
    pub fn ua(&self) -> f64 {
        self.inner.ua()
    }

    /// Returns calibration factors (f_ua for condenser).
    pub fn calib(&self) -> &Calib {
        self.inner.calib()
    }

    /// Sets calibration factors.
    pub fn set_calib(&mut self, calib: Calib) {
        self.inner.set_calib(calib);
    }

    /// Returns the saturation temperature.
    pub fn saturation_temp(&self) -> f64 {
        self.saturation_temp
    }

    /// Sets the saturation temperature.
    pub fn set_saturation_temp(&mut self, temp: f64) {
        self.saturation_temp = temp;
    }

    /// Overrides the effective UA value [W/K] at runtime.
    ///
    /// Sets the UA scale factor so that `UA_nominal × scale = ua_value`.
    /// Used by `MchxCondenserCoil` to apply fan-speed and air-density corrections.
    pub fn set_ua(&mut self, ua_value: f64) {
        let ua_nominal = self.inner.ua_nominal();
        let scale = if ua_nominal > 0.0 {
            ua_value / ua_nominal
        } else {
            1.0
        };
        self.inner.set_ua_scale(scale.max(0.0));
    }

    /// Validates that the outlet quality is <= 1 (fully condensed or subcooled).
    ///
    /// # Arguments
    ///
    /// * `outlet_enthalpy` - Outlet specific enthalpy (J/kg)
    /// * `h_liquid` - Saturated liquid enthalpy at condensing pressure
    /// * `h_vapor` - Saturated vapor enthalpy at condensing pressure
    ///
    /// # Returns
    ///
    /// Returns Ok(true) if fully condensed, Err otherwise
    pub fn validate_outlet_quality(
        &self,
        outlet_enthalpy: f64,
        h_liquid: f64,
        h_vapor: f64,
    ) -> Result<bool, ComponentError> {
        if h_vapor <= h_liquid {
            return Err(ComponentError::NumericalError(
                "Invalid saturation enthalpies".to_string(),
            ));
        }

        let quality = (outlet_enthalpy - h_liquid) / (h_vapor - h_liquid);

        if quality <= 0.0 + 1e-6 {
            Ok(true)
        } else {
            Err(ComponentError::InvalidState(format!(
                "Condenser outlet quality {} > 0 (not fully condensed)",
                quality
            )))
        }
    }

    /// Computes the full thermodynamic state at the hot inlet.
    pub fn hot_inlet_state(&self) -> Result<entropyk_fluids::ThermoState, ComponentError> {
        self.inner.hot_inlet_state()
    }

    /// Computes the full thermodynamic state at the cold inlet.
    pub fn cold_inlet_state(&self) -> Result<entropyk_fluids::ThermoState, ComponentError> {
        self.inner.cold_inlet_state()
    }

    /// Returns the hot side fluid identifier, if set.
    pub fn hot_fluid_id(&self) -> Option<&entropyk_fluids::FluidId> {
        self.inner.hot_fluid_id()
    }

    /// Sets the cold side boundary conditions.
    pub fn set_cold_conditions(&mut self, conditions: super::exchanger::HxSideConditions) {
        self.inner.set_cold_conditions(conditions);
    }

    /// Returns the cold side fluid identifier, if set.
    pub fn cold_fluid_id(&self) -> Option<&entropyk_fluids::FluidId> {
        self.inner.cold_fluid_id()
    }
}

impl Component for Condenser {
    fn compute_residuals(
        &self,
        state: &StateSlice,
        residuals: &mut ResidualVector,
    ) -> Result<(), ComponentError> {
        self.inner.compute_residuals(state, residuals)
    }

    fn jacobian_entries(
        &self,
        state: &StateSlice,
        jacobian: &mut JacobianBuilder,
    ) -> Result<(), ComponentError> {
        self.inner.jacobian_entries(state, jacobian)
    }

    fn n_equations(&self) -> usize {
        self.inner.n_equations()
    }

    fn get_ports(&self) -> &[ConnectedPort] {
        self.inner.get_ports()
    }

    fn set_calib_indices(&mut self, indices: entropyk_core::CalibIndices) {
        self.inner.set_calib_indices(indices);
    }

    fn port_mass_flows(
        &self,
        state: &StateSlice,
    ) -> Result<Vec<entropyk_core::MassFlow>, ComponentError> {
        self.inner.port_mass_flows(state)
    }

    fn port_enthalpies(
        &self,
        state: &StateSlice,
    ) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
        self.inner.port_enthalpies(state)
    }

    fn energy_transfers(
        &self,
        state: &StateSlice,
    ) -> Option<(entropyk_core::Power, entropyk_core::Power)> {
        self.inner.energy_transfers(state)
    }
}

impl StateManageable for Condenser {
    fn state(&self) -> OperationalState {
        self.inner.state()
    }

    fn set_state(&mut self, state: OperationalState) -> Result<(), ComponentError> {
        self.inner.set_state(state)
    }

    fn can_transition_to(&self, target: OperationalState) -> bool {
        self.inner.can_transition_to(target)
    }

    fn circuit_id(&self) -> &CircuitId {
        self.inner.circuit_id()
    }

    fn set_circuit_id(&mut self, circuit_id: CircuitId) {
        self.inner.set_circuit_id(circuit_id);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_condenser_creation() {
        let condenser = Condenser::new(10_000.0);
        assert_eq!(condenser.ua(), 10_000.0);
        assert_eq!(condenser.n_equations(), 2);
    }

    #[test]
    fn test_condenser_with_saturation_temp() {
        let condenser = Condenser::with_saturation_temp(10_000.0, 323.15);
        assert_eq!(condenser.saturation_temp(), 323.15);
    }

    #[test]
    fn test_validate_outlet_quality_fully_condensed() {
        let condenser = Condenser::new(10_000.0);

        let h_liquid = 200_000.0;
        let h_vapor = 400_000.0;
        let outlet_h = 200_000.0;

        let result = condenser.validate_outlet_quality(outlet_h, h_liquid, h_vapor);
        assert!(result.is_ok());
    }

    #[test]
    fn test_validate_outlet_quality_subcooled() {
        let condenser = Condenser::new(10_000.0);

        let h_liquid = 200_000.0;
        let h_vapor = 400_000.0;
        let outlet_h = 180_000.0;

        let result = condenser.validate_outlet_quality(outlet_h, h_liquid, h_vapor);
        assert!(result.is_ok());
    }

    #[test]
    fn test_validate_outlet_quality_two_phase() {
        let condenser = Condenser::new(10_000.0);

        let h_liquid = 200_000.0;
        let h_vapor = 400_000.0;
        let outlet_h = 300_000.0;

        let result = condenser.validate_outlet_quality(outlet_h, h_liquid, h_vapor);
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_outlet_quality_superheated() {
        let condenser = Condenser::new(10_000.0);

        let h_liquid = 200_000.0;
        let h_vapor = 400_000.0;
        let outlet_h = 450_000.0;

        let result = condenser.validate_outlet_quality(outlet_h, h_liquid, h_vapor);
        assert!(result.is_err());
    }

    #[test]
    fn test_compute_residuals() {
        let condenser = Condenser::new(10_000.0);

        let state = vec![0.0; 10];
        let mut residuals = vec![0.0; 2];

        let result = condenser.compute_residuals(&state, &mut residuals);
        assert!(result.is_ok());
    }
}
