//! Economizer Component
//!
//! An internal heat exchanger with bypass support for refrigeration systems.
/// Can be switched between ON (active heat exchange), OFF (no flow), and
/// BYPASS (adiabatic pipe) modes.
use super::exchanger::HeatExchanger;
use super::lmtd::{FlowConfiguration, LmtdModel};
use crate::{
    Component, ComponentError, ConnectedPort, JacobianBuilder, OperationalState, ResidualVector,
    StateSlice,
};

/// Economizer (internal heat exchanger) with state machine support.
///
/// The economizer can operate in three modes:
/// - **ON**: Normal heat exchange between suction and liquid lines
/// - **OFF**: No mass flow contribution (component disabled)
/// - **BYPASS**: Adiabatic pipe (P_in = P_out, h_in = h_out)
///
/// # Example
///
/// ```
/// use entropyk_components::heat_exchanger::Economizer;
/// use entropyk_components::OperationalState;
///
/// let mut economizer = Economizer::new(2_000.0);
/// assert_eq!(economizer.state(), OperationalState::On);
///
/// economizer.set_state(OperationalState::Bypass);
/// assert_eq!(economizer.state(), OperationalState::Bypass);
/// ```
#[derive(Debug)]
pub struct Economizer {
    /// Inner heat exchanger with LMTD model
    inner: HeatExchanger<LmtdModel>,
    /// Operational state
    state: OperationalState,
}

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

    /// Creates an economizer in a specific state.
    pub fn with_state(ua: f64, state: OperationalState) -> Self {
        let model = LmtdModel::new(ua, FlowConfiguration::CounterFlow);
        Self {
            inner: HeatExchanger::new(model, "Economizer"),
            state,
        }
    }

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

    /// Returns the UA value.
    pub fn ua(&self) -> f64 {
        self.inner.ua()
    }

    /// Returns the current operational state.
    pub fn state(&self) -> OperationalState {
        self.state
    }

    /// Sets the operational state.
    pub fn set_state(&mut self, state: OperationalState) {
        self.state = state;
    }

    /// Returns true if the economizer is active (ON or BYPASS).
    pub fn is_active(&self) -> bool {
        self.state.is_active()
    }

    /// Returns true if in bypass mode (adiabatic pipe behavior).
    pub fn is_bypass(&self) -> bool {
        self.state.is_bypass()
    }

    /// Returns the mass flow multiplier based on state.
    pub fn mass_flow_multiplier(&self) -> f64 {
        self.state.mass_flow_multiplier()
    }

    /// Computes bypass residuals (P_in = P_out, h_in = h_out).
    fn compute_bypass_residuals(&self, residuals: &mut ResidualVector) {
        residuals[0] = 0.0;
        residuals[1] = 0.0;
        residuals[2] = 0.0;
    }

    /// Computes off residuals (zero flow).
    fn compute_off_residuals(&self, residuals: &mut ResidualVector) {
        residuals[0] = 0.0;
        residuals[1] = 0.0;
        residuals[2] = 0.0;
    }
}

impl Component for Economizer {
    fn compute_residuals(
        &self,
        state: &StateSlice,
        residuals: &mut ResidualVector,
    ) -> Result<(), ComponentError> {
        if residuals.len() < self.n_equations() {
            return Err(ComponentError::InvalidResidualDimensions {
                expected: self.n_equations(),
                actual: residuals.len(),
            });
        }

        match self.state {
            OperationalState::On => self.inner.compute_residuals(state, residuals),
            OperationalState::Off => {
                self.compute_off_residuals(residuals);
                Ok(())
            }
            OperationalState::Bypass => {
                self.compute_bypass_residuals(residuals);
                Ok(())
            }
        }
    }

    fn jacobian_entries(
        &self,
        state: &StateSlice,
        jacobian: &mut JacobianBuilder,
    ) -> Result<(), ComponentError> {
        match self.state {
            OperationalState::On => self.inner.jacobian_entries(state, jacobian),
            OperationalState::Off | OperationalState::Bypass => Ok(()),
        }
    }

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

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

    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)
    }

    fn signature(&self) -> String {
        self.inner.signature()
    }

    fn to_params(&self) -> crate::ComponentParams {
        self.inner.to_params()
    }
}

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

    #[test]
    fn test_economizer_creation() {
        let economizer = Economizer::new(2_000.0);
        assert_eq!(economizer.ua(), 2_000.0);
        assert_eq!(economizer.state(), OperationalState::On);
    }

    #[test]
    fn test_economizer_with_state() {
        let economizer = Economizer::with_state(2_000.0, OperationalState::Bypass);
        assert_eq!(economizer.state(), OperationalState::Bypass);
    }

    #[test]
    fn test_state_transitions() {
        let mut economizer = Economizer::new(2_000.0);

        assert!(economizer.is_active());
        assert!(!economizer.is_bypass());

        economizer.set_state(OperationalState::Bypass);
        assert!(economizer.is_active());
        assert!(economizer.is_bypass());

        economizer.set_state(OperationalState::Off);
        assert!(!economizer.is_active());
        assert!(!economizer.is_bypass());
    }

    #[test]
    fn test_mass_flow_multiplier() {
        let mut economizer = Economizer::new(2_000.0);

        assert_eq!(economizer.mass_flow_multiplier(), 1.0);

        economizer.set_state(OperationalState::Bypass);
        assert_eq!(economizer.mass_flow_multiplier(), 1.0);

        economizer.set_state(OperationalState::Off);
        assert_eq!(economizer.mass_flow_multiplier(), 0.0);
    }

    #[test]
    fn test_compute_residuals_on() {
        let economizer = Economizer::new(2_000.0);

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

        let result = economizer.compute_residuals(&state, &mut residuals);
        assert!(matches!(result, Err(ComponentError::InvalidState(_))));
    }

    #[test]
    fn test_compute_residuals_bypass() {
        let economizer = Economizer::with_state(2_000.0, OperationalState::Bypass);

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

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

    #[test]
    fn test_compute_residuals_off() {
        let economizer = Economizer::with_state(2_000.0, OperationalState::Off);

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

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

    #[test]
    fn test_n_equations() {
        let economizer = Economizer::new(2_000.0);
        assert_eq!(economizer.n_equations(), 2);
    }
}
