//! Shell-and-tube heat exchanger rating via Bell–Delaware shell-side factors.
//!
//! Computes shell-side HTC `h_s = h_ideal · J_C · J_L · J_B · J_R · J_S` and a
//! combined UA with tube-side Gnielinski / Cooper / Shah as selected.

use super::bphx_correlation::{BphxCorrelation, CorrelationParams, FlowRegime};
use super::correlation_registry::CorrelationId;
use super::pool_boiling::{cooper_1984, PoolBoilingInput};

/// Geometric inputs for Bell–Delaware correction factors.
#[derive(Debug, Clone, Copy)]
pub struct BellDelawareGeometry {
    /// Fraction of tubes in cross-flow (F_C ≈ 1 − 2 F_W).
    pub f_c: f64,
    /// Leakage area ratio r_s = S_sb / (S_sb + S_tb).
    pub r_s: f64,
    /// Leakage/main stream area ratio r_lm = (S_sb + S_tb) / S_m.
    pub r_lm: f64,
    /// Bypass correction J_B (typical 0.7–0.9).
    pub j_b: f64,
    /// Laminar gradient correction J_R (1.0 if Re_s ≥ 100).
    pub j_r: f64,
    /// Unequal baffle spacing correction J_S (1.0 if uniform).
    pub j_s: f64,
    /// Ideal cross-flow HTC [W/(m²·K)] before corrections.
    pub h_ideal: f64,
    /// Shell-side area [m²].
    pub area_shell_m2: f64,
    /// Tube-side area [m²].
    pub area_tube_m2: f64,
}

impl Default for BellDelawareGeometry {
    fn default() -> Self {
        Self {
            f_c: 0.9,
            r_s: 0.4,
            r_lm: 0.3,
            j_b: 0.8,
            j_r: 1.0,
            j_s: 1.0,
            h_ideal: 2500.0,
            area_shell_m2: 30.0,
            area_tube_m2: 25.0,
        }
    }
}

/// Bell–Delaware correction factors.
#[derive(Debug, Clone, Copy)]
pub struct BellDelawareFactors {
    /// Configuration factor J_C.
    pub j_c: f64,
    /// Leakage factor J_L.
    pub j_l: f64,
    /// Bypass factor J_B.
    pub j_b: f64,
    /// Laminar factor J_R.
    pub j_r: f64,
    /// Spacing factor J_S.
    pub j_s: f64,
}

/// Computes J-factors from geometry (Taborek / Delaware handbook forms).
pub fn bell_delaware_factors(geom: &BellDelawareGeometry) -> BellDelawareFactors {
    let j_c = 0.55 + 0.72 * geom.f_c.clamp(0.0, 1.0);
    let j_l = 0.44 * (1.0 - geom.r_s) + (1.0 - 0.44 * (1.0 - geom.r_s)) * (-2.2 * geom.r_lm).exp();
    BellDelawareFactors {
        j_c: j_c.clamp(0.65, 1.15),
        j_l: j_l.clamp(0.2, 1.0),
        j_b: geom.j_b.clamp(0.7, 0.9),
        j_r: geom.j_r.clamp(0.7, 1.0),
        j_s: geom.j_s.clamp(0.8, 1.0),
    }
}

/// Shell-side HTC after Bell–Delaware corrections [W/(m²·K)].
pub fn shell_side_htc(geom: &BellDelawareGeometry) -> f64 {
    let f = bell_delaware_factors(geom);
    geom.h_ideal * f.j_c * f.j_l * f.j_b * f.j_r * f.j_s
}

/// Tube-side HTC using a registered correlation [W/(m²·K)].
pub fn tube_side_htc(
    correlation: CorrelationId,
    params: &CorrelationParams,
    pool: Option<&PoolBoilingInput>,
) -> f64 {
    match correlation {
        CorrelationId::Cooper1984 => pool.and_then(|p| cooper_1984(p).ok()).unwrap_or(3000.0),
        CorrelationId::Gnielinski1976 => BphxCorrelation::Gnielinski1976.compute_htc(params).h,
        CorrelationId::Shah2009 => BphxCorrelation::Shah2009.compute_htc(params).h,
        CorrelationId::Shah1979 => BphxCorrelation::Shah1979.compute_htc(params).h,
        CorrelationId::Cavallini2006 => BphxCorrelation::Cavallini2006.compute_htc(params).h,
        CorrelationId::Kandlikar1990 => BphxCorrelation::Kandlikar1990.compute_htc(params).h,
        _ => BphxCorrelation::Gnielinski1976.compute_htc(params).h,
    }
}

/// Combined UA [W/K] from shell and tube sides (wall resistance neglected).
pub fn shell_and_tube_ua(geom: &BellDelawareGeometry, h_tube: f64) -> f64 {
    let h_shell = shell_side_htc(geom);
    let r = 1.0 / (h_shell.max(1.0) * geom.area_shell_m2.max(1e-9))
        + 1.0 / (h_tube.max(1.0) * geom.area_tube_m2.max(1e-9));
    1.0 / r
}

/// Rating helper wrapping Bell–Delaware + tube correlation.
#[derive(Debug, Clone)]
pub struct ShellAndTubeHx {
    geom: BellDelawareGeometry,
    tube_correlation: CorrelationId,
    last_ua: f64,
}

impl ShellAndTubeHx {
    /// Creates a shell-and-tube rater with default geometry.
    pub fn new(geom: BellDelawareGeometry) -> Self {
        Self {
            geom,
            tube_correlation: CorrelationId::Gnielinski1976,
            last_ua: 0.0,
        }
    }

    /// Selects tube-side correlation.
    pub fn with_tube_correlation(mut self, id: CorrelationId) -> Self {
        self.tube_correlation = id;
        self
    }

    /// Rates UA from operating tube-side params.
    pub fn rate_ua(&mut self, params: &CorrelationParams) -> f64 {
        let h_tube = tube_side_htc(self.tube_correlation, params, None);
        self.last_ua = shell_and_tube_ua(&self.geom, h_tube);
        self.last_ua
    }

    /// Last computed UA [W/K].
    pub fn ua(&self) -> f64 {
        self.last_ua
    }

    /// Geometry accessor.
    pub fn geometry(&self) -> &BellDelawareGeometry {
        &self.geom
    }
}

/// Default condensation CorrelationParams for tube-side rating.
pub fn default_tube_params(regime: FlowRegime) -> CorrelationParams {
    CorrelationParams {
        regime,
        mass_flux: 200.0,
        ..CorrelationParams::default()
    }
}

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

    #[test]
    fn factors_in_expected_bands() {
        let f = bell_delaware_factors(&BellDelawareGeometry::default());
        assert!(f.j_c >= 0.65 && f.j_c <= 1.15);
        assert!(f.j_l >= 0.2 && f.j_l <= 1.0);
    }

    #[test]
    fn shell_htc_less_than_ideal() {
        let g = BellDelawareGeometry::default();
        let h = shell_side_htc(&g);
        assert!(h < g.h_ideal);
        assert!(h > 500.0);
    }

    #[test]
    fn rate_ua_positive() {
        let mut hx = ShellAndTubeHx::new(BellDelawareGeometry::default());
        let ua = hx.rate_ua(&default_tube_params(FlowRegime::SinglePhaseLiquid));
        assert!(ua.is_finite() && ua > 1000.0);
    }
}
