//! Physical types using the NewType pattern for compile-time unit safety.
//!
//! This module provides type-safe wrappers around `f64` for physical quantities,
//! preventing accidental mixing of units at compile time.
//!
//! All types store values in SI base units internally:
//! - Pressure: Pascals (Pa)
//! - Temperature: Kelvin (K)
//! - Enthalpy: Joules per kilogram (J/kg)
//! - MassFlow: Kilograms per second (kg/s)

use std::fmt;
use std::ops::{Add, Div, Mul, Sub};

/// Pressure in Pascals (Pa).
///
/// Internally stores the value in Pascals (SI base unit).
/// Provides conversions to/from common units like bar.
///
/// # Example
///
/// ```
/// use entropyk_core::Pressure;
///
/// let p = Pressure::from_bar(1.0);
/// assert_eq!(p.to_pascals(), 100_000.0);
/// assert_eq!(p.to_bar(), 1.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Pressure(pub f64);

impl Pressure {
    /// Creates a Pressure from a value in Pascals.
    pub fn from_pascals(value: f64) -> Self {
        Pressure(value)
    }

    /// Creates a Pressure from a value in bar.
    pub fn from_bar(value: f64) -> Self {
        Pressure(value * 100_000.0)
    }

    /// Creates a Pressure from a value in PSI (pounds per square inch).
    pub fn from_psi(value: f64) -> Self {
        Pressure(value * 6894.75729)
    }

    /// Returns the pressure in Pascals.
    pub fn to_pascals(&self) -> f64 {
        self.0
    }

    /// Returns the pressure in bar.
    pub fn to_bar(&self) -> f64 {
        self.0 / 100_000.0
    }

    /// Returns the pressure in PSI (pounds per square inch).
    pub fn to_psi(&self) -> f64 {
        self.0 / 6894.75729
    }
}

impl fmt::Display for Pressure {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} Pa", self.0)
    }
}

impl From<f64> for Pressure {
    fn from(value: f64) -> Self {
        Pressure(value)
    }
}

impl Add<Pressure> for Pressure {
    type Output = Pressure;

    fn add(self, other: Pressure) -> Pressure {
        Pressure(self.0 + other.0)
    }
}

impl Sub<Pressure> for Pressure {
    type Output = Pressure;

    fn sub(self, other: Pressure) -> Pressure {
        Pressure(self.0 - other.0)
    }
}

impl Mul<f64> for Pressure {
    type Output = Pressure;

    fn mul(self, scalar: f64) -> Pressure {
        Pressure(self.0 * scalar)
    }
}

impl Mul<Pressure> for f64 {
    type Output = Pressure;

    fn mul(self, p: Pressure) -> Pressure {
        Pressure(self * p.0)
    }
}

impl Div<f64> for Pressure {
    type Output = Pressure;

    fn div(self, scalar: f64) -> Pressure {
        Pressure(self.0 / scalar)
    }
}

/// Temperature in Kelvin (K).
///
/// Internally stores the value in Kelvin (SI base unit).
/// Provides conversions to/from Celsius.
///
/// # Example
///
/// ```
/// use entropyk_core::Temperature;
///
/// let t = Temperature::from_celsius(0.0);
/// assert_eq!(t.to_kelvin(), 273.15);
/// assert_eq!(t.to_celsius(), 0.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Temperature(pub f64);

impl Temperature {
    /// Creates a Temperature from a value in Kelvin.
    pub fn from_kelvin(value: f64) -> Self {
        Temperature(value)
    }

    /// Creates a Temperature from a value in Celsius.
    pub fn from_celsius(value: f64) -> Self {
        Temperature(value + 273.15)
    }

    /// Creates a Temperature from a value in Fahrenheit.
    pub fn from_fahrenheit(value: f64) -> Self {
        Temperature((value - 32.0) * 5.0 / 9.0 + 273.15)
    }

    /// Returns the temperature in Kelvin.
    pub fn to_kelvin(&self) -> f64 {
        self.0
    }

    /// Returns the temperature in Celsius.
    pub fn to_celsius(&self) -> f64 {
        self.0 - 273.15
    }

    /// Returns the temperature in Fahrenheit.
    pub fn to_fahrenheit(&self) -> f64 {
        (self.0 - 273.15) * 9.0 / 5.0 + 32.0
    }
}

impl fmt::Display for Temperature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} K", self.0)
    }
}

impl From<f64> for Temperature {
    fn from(value: f64) -> Self {
        Temperature(value)
    }
}

impl Add<Temperature> for Temperature {
    type Output = Temperature;

    fn add(self, other: Temperature) -> Temperature {
        Temperature(self.0 + other.0)
    }
}

impl Sub<Temperature> for Temperature {
    type Output = Temperature;

    fn sub(self, other: Temperature) -> Temperature {
        Temperature(self.0 - other.0)
    }
}

impl Mul<f64> for Temperature {
    type Output = Temperature;

    fn mul(self, scalar: f64) -> Temperature {
        Temperature(self.0 * scalar)
    }
}

impl Mul<Temperature> for f64 {
    type Output = Temperature;

    fn mul(self, t: Temperature) -> Temperature {
        Temperature(self * t.0)
    }
}

impl Div<f64> for Temperature {
    type Output = Temperature;

    fn div(self, scalar: f64) -> Temperature {
        Temperature(self.0 / scalar)
    }
}

/// Specific enthalpy in Joules per kilogram (J/kg).
///
/// Internally stores the value in Joules per kilogram (SI base unit).
///
/// # Example
///
/// ```
/// use entropyk_core::Enthalpy;
///
/// let h = Enthalpy::from_joules_per_kg(1000.0);
/// assert_eq!(h.to_joules_per_kg(), 1000.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Enthalpy(pub f64);

impl Enthalpy {
    /// Creates an Enthalpy from a value in Joules per kilogram.
    pub fn from_joules_per_kg(value: f64) -> Self {
        Enthalpy(value)
    }

    /// Creates an Enthalpy from a value in kilojoules per kilogram.
    pub fn from_kilojoules_per_kg(value: f64) -> Self {
        Enthalpy(value * 1_000.0)
    }

    /// Returns the enthalpy in Joules per kilogram.
    pub fn to_joules_per_kg(&self) -> f64 {
        self.0
    }

    /// Returns the enthalpy in kilojoules per kilogram.
    pub fn to_kilojoules_per_kg(&self) -> f64 {
        self.0 / 1_000.0
    }
}

impl fmt::Display for Enthalpy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} J/kg", self.0)
    }
}

impl From<f64> for Enthalpy {
    fn from(value: f64) -> Self {
        Enthalpy(value)
    }
}

impl Add<Enthalpy> for Enthalpy {
    type Output = Enthalpy;

    fn add(self, other: Enthalpy) -> Enthalpy {
        Enthalpy(self.0 + other.0)
    }
}

impl Sub<Enthalpy> for Enthalpy {
    type Output = Enthalpy;

    fn sub(self, other: Enthalpy) -> Enthalpy {
        Enthalpy(self.0 - other.0)
    }
}

impl Mul<f64> for Enthalpy {
    type Output = Enthalpy;

    fn mul(self, scalar: f64) -> Enthalpy {
        Enthalpy(self.0 * scalar)
    }
}

impl Mul<Enthalpy> for f64 {
    type Output = Enthalpy;

    fn mul(self, h: Enthalpy) -> Enthalpy {
        Enthalpy(self * h.0)
    }
}

impl Div<f64> for Enthalpy {
    type Output = Enthalpy;

    fn div(self, scalar: f64) -> Enthalpy {
        Enthalpy(self.0 / scalar)
    }
}

/// Minimum mass flow used in denominators to avoid division by zero (zero-flow regularization).
///
/// When mass flow is zero or below this value, use [`MassFlow::regularized`] in any expression
/// that divides by mass flow (e.g. Q/ṁ, ΔP/ṁ²) or by quantities derived from it (e.g. Reynolds,
/// capacity rate C = ṁ·Cp). This prevents NaN/Inf while preserving solver convergence.
///
/// Value: 1e-12 kg/s (small enough to not affect physical results when ṁ >> ε).
pub const MIN_MASS_FLOW_REGULARIZATION_KG_S: f64 = 1e-12;

/// Mass flow rate in kilograms per second (kg/s).
///
/// Internally stores the value in kilograms per second (SI base unit).
///
/// # Zero-flow regularization
///
/// When dividing by mass flow (or using it in denominators), use [`MassFlow::regularized`] so that
/// zero-flow branches do not cause division by zero. See [`MIN_MASS_FLOW_REGULARIZATION_KG_S`].
///
/// # Example
///
/// ```
/// use entropyk_core::MassFlow;
///
/// let m = MassFlow::from_kg_per_s(0.5);
/// assert_eq!(m.to_kg_per_s(), 0.5);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct MassFlow(pub f64);

impl MassFlow {
    /// Creates a MassFlow from a value in kilograms per second.
    pub fn from_kg_per_s(value: f64) -> Self {
        MassFlow(value)
    }

    /// Creates a MassFlow from a value in grams per second.
    pub fn from_grams_per_s(value: f64) -> Self {
        MassFlow(value / 1_000.0)
    }

    /// Returns the mass flow rate in kilograms per second.
    pub fn to_kg_per_s(&self) -> f64 {
        self.0
    }

    /// Returns the mass flow rate in grams per second.
    pub fn to_grams_per_s(&self) -> f64 {
        self.0 * 1_000.0
    }

    /// Returns mass flow clamped to at least [`MIN_MASS_FLOW_REGULARIZATION_KG_S`] for use in denominators.
    ///
    /// Use this whenever dividing by mass flow (e.g. Q/ṁ) or by a quantity derived from it (e.g. Re ∝ ṁ)
    /// to avoid division by zero when the branch has zero flow (e.g. component in Off state).
    #[must_use]
    pub fn regularized(self) -> Self {
        MassFlow(self.0.max(MIN_MASS_FLOW_REGULARIZATION_KG_S))
    }
}

impl fmt::Display for MassFlow {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} kg/s", self.0)
    }
}

impl From<f64> for MassFlow {
    fn from(value: f64) -> Self {
        MassFlow(value)
    }
}

impl Add<MassFlow> for MassFlow {
    type Output = MassFlow;

    fn add(self, other: MassFlow) -> MassFlow {
        MassFlow(self.0 + other.0)
    }
}

impl Sub<MassFlow> for MassFlow {
    type Output = MassFlow;

    fn sub(self, other: MassFlow) -> MassFlow {
        MassFlow(self.0 - other.0)
    }
}

impl Mul<f64> for MassFlow {
    type Output = MassFlow;

    fn mul(self, scalar: f64) -> MassFlow {
        MassFlow(self.0 * scalar)
    }
}

impl Mul<MassFlow> for f64 {
    type Output = MassFlow;

    fn mul(self, m: MassFlow) -> MassFlow {
        MassFlow(self * m.0)
    }
}

impl Div<f64> for MassFlow {
    type Output = MassFlow;

    fn div(self, scalar: f64) -> MassFlow {
        MassFlow(self.0 / scalar)
    }
}

/// Power in Watts (W).
///
/// Internally stores the value in Watts (SI base unit).
/// Provides conversions to/from common units like kilowatts.
///
/// # Example
///
/// ```
/// use entropyk_core::Power;
///
/// let p = Power::from_kilowatts(1.0);
/// assert_eq!(p.to_watts(), 1000.0);
/// assert_eq!(p.to_kilowatts(), 1.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Power(pub f64);

impl Power {
    /// Creates a Power from a value in Watts.
    pub fn from_watts(value: f64) -> Self {
        Power(value)
    }

    /// Creates a Power from a value in kilowatts.
    pub fn from_kilowatts(value: f64) -> Self {
        Power(value * 1_000.0)
    }

    /// Creates a Power from a value in megawatts.
    pub fn from_megawatts(value: f64) -> Self {
        Power(value * 1_000_000.0)
    }

    /// Returns the power in Watts.
    pub fn to_watts(&self) -> f64 {
        self.0
    }

    /// Returns the power in kilowatts.
    pub fn to_kilowatts(&self) -> f64 {
        self.0 / 1_000.0
    }

    /// Returns the power in megawatts.
    pub fn to_megawatts(&self) -> f64 {
        self.0 / 1_000_000.0
    }
}

impl fmt::Display for Power {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} W", self.0)
    }
}

impl From<f64> for Power {
    fn from(value: f64) -> Self {
        Power(value)
    }
}

impl Add<Power> for Power {
    type Output = Power;

    fn add(self, other: Power) -> Power {
        Power(self.0 + other.0)
    }
}

impl Sub<Power> for Power {
    type Output = Power;

    fn sub(self, other: Power) -> Power {
        Power(self.0 - other.0)
    }
}

impl Mul<f64> for Power {
    type Output = Power;

    fn mul(self, scalar: f64) -> Power {
        Power(self.0 * scalar)
    }
}

impl Mul<Power> for f64 {
    type Output = Power;

    fn mul(self, p: Power) -> Power {
        Power(self * p.0)
    }
}

impl Div<f64> for Power {
    type Output = Power;

    fn div(self, scalar: f64) -> Power {
        Power(self.0 / scalar)
    }
}

/// Thermal conductance in Watts per Kelvin (W/K).
///
/// Represents the heat transfer coefficient (UA value) for thermal coupling
/// between circuits or components.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct ThermalConductance(pub f64);

impl ThermalConductance {
    /// Creates a ThermalConductance from a value in Watts per Kelvin (W/K).
    pub fn from_watts_per_kelvin(value: f64) -> Self {
        ThermalConductance(value)
    }

    /// Creates a ThermalConductance from a value in kilowatts per Kelvin (kW/K).
    pub fn from_kilowatts_per_kelvin(value: f64) -> Self {
        ThermalConductance(value * 1_000.0)
    }

    /// Returns the thermal conductance in Watts per Kelvin.
    pub fn to_watts_per_kelvin(&self) -> f64 {
        self.0
    }

    /// Returns the thermal conductance in kilowatts per Kelvin.
    pub fn to_kilowatts_per_kelvin(&self) -> f64 {
        self.0 / 1_000.0
    }
}

impl fmt::Display for ThermalConductance {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} W/K", self.0)
    }
}

impl From<f64> for ThermalConductance {
    fn from(value: f64) -> Self {
        ThermalConductance(value)
    }
}

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

    // ==================== PRESSURE TESTS ====================

    #[test]
    fn test_pressure_from_pascals() {
        let p = Pressure::from_pascals(101325.0);
        assert_relative_eq!(p.0, 101325.0, epsilon = 1e-10);
        assert_relative_eq!(p.to_pascals(), 101325.0, epsilon = 1e-10);
    }

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

    #[test]
    fn test_pressure_to_bar() {
        let p = Pressure::from_pascals(100_000.0);
        assert_relative_eq!(p.to_bar(), 1.0, epsilon = 1e-6);
    }

    #[test]
    fn test_pressure_round_trip() {
        let p1 = Pressure::from_bar(2.5);
        let bar = p1.to_bar();
        assert_relative_eq!(bar, 2.5, epsilon = 1e-6);
    }

    #[test]
    fn test_pressure_display() {
        let p = Pressure::from_pascals(101325.0);
        assert_eq!(format!("{}", p), "101325 Pa");
    }

    #[test]
    fn test_pressure_from_f64() {
        let p: Pressure = 101325.0.into();
        assert_relative_eq!(p.to_pascals(), 101325.0, epsilon = 1e-10);
    }

    #[test]
    fn test_pressure_add() {
        let p1 = Pressure::from_pascals(100_000.0);
        let p2 = Pressure::from_pascals(50_000.0);
        let p3 = p1 + p2;
        assert_relative_eq!(p3.to_pascals(), 150_000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_pressure_sub() {
        let p1 = Pressure::from_pascals(100_000.0);
        let p2 = Pressure::from_pascals(30_000.0);
        let p3 = p1 - p2;
        assert_relative_eq!(p3.to_pascals(), 70_000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_pressure_mul() {
        let p = Pressure::from_pascals(50_000.0);
        let p2 = p * 2.0;
        assert_relative_eq!(p2.to_pascals(), 100_000.0, epsilon = 1e-10);

        // Test reverse multiplication
        let p3 = 2.0 * p;
        assert_relative_eq!(p3.to_pascals(), 100_000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_pressure_div() {
        let p = Pressure::from_pascals(100_000.0);
        let p2 = p / 2.0;
        assert_relative_eq!(p2.to_pascals(), 50_000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_pressure_psi_conversions() {
        // 1 atm = 14.696 psi ≈ 101325 Pa
        let p = Pressure::from_psi(14.696);
        assert_relative_eq!(p.to_pascals(), 101325.0, epsilon = 1.0);
        assert_relative_eq!(p.to_psi(), 14.696, epsilon = 1e-3);

        // Round trip test
        let p2 = Pressure::from_psi(100.0);
        assert_relative_eq!(p2.to_psi(), 100.0, epsilon = 1e-6);
    }

    // ==================== TEMPERATURE TESTS ====================

    #[test]
    fn test_temperature_from_kelvin() {
        let t = Temperature::from_kelvin(298.15);
        assert_relative_eq!(t.0, 298.15, epsilon = 1e-10);
        assert_relative_eq!(t.to_kelvin(), 298.15, epsilon = 1e-10);
    }

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

    #[test]
    fn test_temperature_to_celsius() {
        let t = Temperature::from_kelvin(273.15);
        assert_relative_eq!(t.to_celsius(), 0.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_round_trip() {
        let t1 = Temperature::from_celsius(25.0);
        let celsius = t1.to_celsius();
        assert_relative_eq!(celsius, 25.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_fahrenheit_conversions() {
        // 32°F = 0°C = 273.15K
        let t_freezing = Temperature::from_fahrenheit(32.0);
        assert_relative_eq!(t_freezing.to_celsius(), 0.0, epsilon = 1e-10);
        assert_relative_eq!(t_freezing.to_kelvin(), 273.15, epsilon = 1e-10);

        // 212°F = 100°C = 373.15K
        let t_boiling = Temperature::from_fahrenheit(212.0);
        assert_relative_eq!(t_boiling.to_celsius(), 100.0, epsilon = 1e-10);
        assert_relative_eq!(t_boiling.to_fahrenheit(), 212.0, epsilon = 1e-10);

        // Round trip
        let t1 = Temperature::from_kelvin(300.0);
        let f = t1.to_fahrenheit();
        let t2 = Temperature::from_fahrenheit(f);
        assert_relative_eq!(t1.to_kelvin(), t2.to_kelvin(), epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_display() {
        let t = Temperature::from_kelvin(298.15);
        assert_eq!(format!("{}", t), "298.15 K");
    }

    #[test]
    fn test_temperature_add() {
        let t1 = Temperature::from_kelvin(300.0);
        let t2 = Temperature::from_kelvin(10.0);
        let t3 = t1 + t2;
        assert_relative_eq!(t3.to_kelvin(), 310.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_sub() {
        let t1 = Temperature::from_kelvin(300.0);
        let t2 = Temperature::from_kelvin(50.0);
        let t3 = t1 - t2;
        assert_relative_eq!(t3.to_kelvin(), 250.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_mul() {
        let t = Temperature::from_kelvin(100.0);
        let t2 = t * 2.0;
        assert_relative_eq!(t2.to_kelvin(), 200.0, epsilon = 1e-10);

        // Test reverse multiplication
        let t3 = 2.0 * t;
        assert_relative_eq!(t3.to_kelvin(), 200.0, epsilon = 1e-10);
    }

    #[test]
    fn test_temperature_div() {
        let t = Temperature::from_kelvin(300.0);
        let t2 = t / 3.0;
        assert_relative_eq!(t2.to_kelvin(), 100.0, epsilon = 1e-10);
    }

    // ==================== ENTHALPY TESTS ====================

    #[test]
    fn test_enthalpy_from_joules_per_kg() {
        let h = Enthalpy::from_joules_per_kg(1000.0);
        assert_relative_eq!(h.0, 1000.0, epsilon = 1e-10);
        assert_relative_eq!(h.to_joules_per_kg(), 1000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_enthalpy_display() {
        let h = Enthalpy::from_joules_per_kg(250000.0);
        assert_eq!(format!("{}", h), "250000 J/kg");
    }

    #[test]
    fn test_enthalpy_arithmetic() {
        let h1 = Enthalpy::from_joules_per_kg(1000.0);
        let h2 = Enthalpy::from_joules_per_kg(500.0);
        let h3 = h1 + h2;
        assert_relative_eq!(h3.to_joules_per_kg(), 1500.0, epsilon = 1e-10);

        let h4 = h1 - h2;
        assert_relative_eq!(h4.to_joules_per_kg(), 500.0, epsilon = 1e-10);

        let h5 = h1 * 2.0;
        assert_relative_eq!(h5.to_joules_per_kg(), 2000.0, epsilon = 1e-10);

        let h6 = h1 / 2.0;
        assert_relative_eq!(h6.to_joules_per_kg(), 500.0, epsilon = 1e-10);

        // Test reverse multiplication
        let h7 = 2.0 * h1;
        assert_relative_eq!(h7.to_joules_per_kg(), 2000.0, epsilon = 1e-10);
    }

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

    // ==================== MASS FLOW TESTS ====================

    #[test]
    fn test_mass_flow_from_kg_per_s() {
        let m = MassFlow::from_kg_per_s(0.5);
        assert_relative_eq!(m.0, 0.5, epsilon = 1e-10);
        assert_relative_eq!(m.to_kg_per_s(), 0.5, epsilon = 1e-10);
    }

    #[test]
    fn test_mass_flow_display() {
        let m = MassFlow::from_kg_per_s(0.1);
        assert_eq!(format!("{}", m), "0.1 kg/s");
    }

    #[test]
    fn test_mass_flow_arithmetic() {
        let m1 = MassFlow::from_kg_per_s(1.0);
        let m2 = MassFlow::from_kg_per_s(0.5);
        let m3 = m1 + m2;
        assert_relative_eq!(m3.to_kg_per_s(), 1.5, epsilon = 1e-10);

        let m4 = m1 - m2;
        assert_relative_eq!(m4.to_kg_per_s(), 0.5, epsilon = 1e-10);

        let m5 = m1 * 2.0;
        assert_relative_eq!(m5.to_kg_per_s(), 2.0, epsilon = 1e-10);

        let m6 = m1 / 2.0;
        assert_relative_eq!(m6.to_kg_per_s(), 0.5, epsilon = 1e-10);

        // Test reverse multiplication
        let m7 = 2.0 * m1;
        assert_relative_eq!(m7.to_kg_per_s(), 2.0, epsilon = 1e-10);
    }

    #[test]
    fn test_mass_flow_unit_conversions() {
        let m1 = MassFlow::from_grams_per_s(500.0);
        assert_relative_eq!(m1.to_kg_per_s(), 0.5, epsilon = 1e-6);
        assert_relative_eq!(m1.to_grams_per_s(), 500.0, epsilon = 1e-6);
    }

    #[test]
    fn test_mass_flow_regularized() {
        use super::MIN_MASS_FLOW_REGULARIZATION_KG_S;
        let zero = MassFlow::from_kg_per_s(0.0);
        let r = zero.regularized();
        assert_relative_eq!(r.to_kg_per_s(), MIN_MASS_FLOW_REGULARIZATION_KG_S, epsilon = 1e-15);
        let small = MassFlow::from_kg_per_s(1e-14);
        let r2 = small.regularized();
        assert_relative_eq!(r2.to_kg_per_s(), MIN_MASS_FLOW_REGULARIZATION_KG_S, epsilon = 1e-15);
        let normal = MassFlow::from_kg_per_s(0.5);
        let r3 = normal.regularized();
        assert_relative_eq!(r3.to_kg_per_s(), 0.5, epsilon = 1e-10);
    }

    // ==================== TYPE SAFETY TESTS ====================

    #[test]
    fn test_pressure_not_equal_to_temperature() {
        // This test ensures Pressure and Temperature are distinct types
        let p = Pressure::from_pascals(100_000.0);
        let t = Temperature::from_kelvin(100.0);

        // They should have different internal representations if units differ
        // But more importantly, this test would fail at compile time if we tried:
        // let x: Pressure = t; // Compile error!
        assert_ne!(p.0, t.0);
    }

    #[test]
    fn test_clone_and_copy() {
        let p1 = Pressure::from_pascals(100_000.0);
        let p2 = p1; // Copy
        let p3 = p1.clone(); // Clone

        assert_relative_eq!(p1.to_pascals(), p2.to_pascals(), epsilon = 1e-10);
        assert_relative_eq!(p1.to_pascals(), p3.to_pascals(), epsilon = 1e-10);
    }

    #[test]
    fn test_partial_ord() {
        let p1 = Pressure::from_pascals(100_000.0);
        let p2 = Pressure::from_pascals(200_000.0);

        assert!(p1 < p2);
        assert!(p2 > p1);
        assert!(p1 == Pressure::from_pascals(100_000.0));
    }

    #[test]
    fn test_partial_ord_with_nan() {
        let p1 = Pressure::from_pascals(100_000.0);
        let p_nan = Pressure(f64::NAN);

        // NaN comparisons should return false for all ordering operators
        assert!(!(p_nan < p1));
        assert!(!(p_nan > p1));
        assert!(!(p_nan == p1));
        assert!(!(p1 < p_nan));
        assert!(!(p1 > p_nan));
        assert!(!(p1 == p_nan));

        // NaN should not equal itself
        assert!(!(p_nan == p_nan));
    }

    // ==================== EDGE CASES ====================

    #[test]
    fn test_zero_values() {
        let p = Pressure::from_pascals(0.0);
        assert_relative_eq!(p.to_pascals(), 0.0, epsilon = 1e-10);
        assert_relative_eq!(p.to_bar(), 0.0, epsilon = 1e-10);

        let t = Temperature::from_kelvin(0.0);
        assert_relative_eq!(t.to_kelvin(), 0.0, epsilon = 1e-10);
        assert_relative_eq!(t.to_celsius(), -273.15, epsilon = 1e-10);
    }

    #[test]
    fn test_negative_values() {
        // Negative pressure doesn't make physical sense but is allowed by the type
        let p = Pressure::from_pascals(-1000.0);
        assert_relative_eq!(p.to_pascals(), -1000.0, epsilon = 1e-10);

        // Negative temperature (below absolute zero) doesn't make sense
        let t = Temperature::from_kelvin(-1.0);
        assert_relative_eq!(t.to_kelvin(), -1.0, epsilon = 1e-10);
    }

    #[test]
    fn test_very_large_values() {
        let p = Pressure::from_pascals(1e15);
        assert_relative_eq!(p.to_pascals(), 1e15, epsilon = 1e5);

        let t = Temperature::from_kelvin(1e9);
        assert_relative_eq!(t.to_kelvin(), 1e9, epsilon = 1e-1);
    }

    #[test]
    fn test_very_small_values() {
        let p = Pressure::from_pascals(1e-10);
        assert_relative_eq!(p.to_pascals(), 1e-10, epsilon = 1e-15);

        let m = MassFlow::from_kg_per_s(1e-12);
        assert_relative_eq!(m.to_kg_per_s(), 1e-12, epsilon = 1e-17);
    }

    // ==================== POWER TESTS ====================

    #[test]
    fn test_power_from_watts() {
        let p = Power::from_watts(1000.0);
        assert_relative_eq!(p.0, 1000.0, epsilon = 1e-10);
        assert_relative_eq!(p.to_watts(), 1000.0, epsilon = 1e-10);
    }

    #[test]
    fn test_power_from_kilowatts() {
        let p = Power::from_kilowatts(1.0);
        assert_relative_eq!(p.to_watts(), 1000.0, epsilon = 1e-6);
        assert_relative_eq!(p.to_kilowatts(), 1.0, epsilon = 1e-6);
    }

    #[test]
    fn test_power_from_megawatts() {
        let p = Power::from_megawatts(1.0);
        assert_relative_eq!(p.to_watts(), 1_000_000.0, epsilon = 1e-6);
        assert_relative_eq!(p.to_megawatts(), 1.0, epsilon = 1e-6);
    }

    #[test]
    fn test_power_display() {
        let p = Power::from_watts(5000.0);
        assert_eq!(format!("{}", p), "5000 W");
    }

    #[test]
    fn test_power_arithmetic() {
        let p1 = Power::from_watts(1000.0);
        let p2 = Power::from_watts(500.0);
        let p3 = p1 + p2;
        assert_relative_eq!(p3.to_watts(), 1500.0, epsilon = 1e-10);

        let p4 = p1 - p2;
        assert_relative_eq!(p4.to_watts(), 500.0, epsilon = 1e-10);

        let p5 = p1 * 2.0;
        assert_relative_eq!(p5.to_watts(), 2000.0, epsilon = 1e-10);

        let p6 = p1 / 2.0;
        assert_relative_eq!(p6.to_watts(), 500.0, epsilon = 1e-10);

        let p7 = 2.0 * p1;
        assert_relative_eq!(p7.to_watts(), 2000.0, epsilon = 1e-10);
    }
}
