use thiserror::Error;

use crate::builder::SystemBuilderError;

/// Unified error type for all Entropyk operations.
///
/// This enum wraps all possible errors that can occur when using the library,
/// providing a single error type for the public API.
#[derive(Error, Debug)]
pub enum ThermoError {
    /// Error from component operations.
    #[error("Component error: {0}")]
    Component(entropyk_components::ComponentError),

    /// Error from solver operations.
    #[error("Solver error: {0}")]
    Solver(entropyk_solver::SolverError),

    /// Error from fluid property calculations.
    #[error("Fluid error: {0}")]
    Fluid(entropyk_fluids::FluidError),

    /// Error from topology operations.
    #[error("Topology error: {0}")]
    Topology(entropyk_solver::TopologyError),

    /// Error adding an edge to the system.
    #[error("Edge error: {0}")]
    AddEdge(entropyk_solver::AddEdgeError),

    /// Error from connection operations.
    #[error("Connection error: {0}")]
    Connection(entropyk_components::ConnectionError),

    /// Error from constraint operations.
    #[error("Constraint error: {0}")]
    Constraint(entropyk_solver::ConstraintError),

    /// Error from initialization.
    #[error("Initialization error: {0}")]
    Initialization(entropyk_solver::InitializerError),

    /// Error from calibration validation.
    #[error("Calibration error: {0}")]
    Calibration(entropyk_core::CalibValidationError),

    /// Error from mixture operations.
    #[error("Mixture error: {0}")]
    Mixture(entropyk_fluids::MixtureError),

    /// Error from system builder operations.
    #[error("Builder error: {0}")]
    Builder(SystemBuilderError),

    /// Invalid input was provided.
    #[error("Invalid input: {0}")]
    InvalidInput(String),

    /// Operation is not supported.
    #[error("Operation not supported: {0}")]
    NotSupported(String),

    /// System was not finalized before an operation.
    #[error("System must be finalized before this operation")]
    NotFinalized,

    /// Simulation validation error (e.g., mass/energy balance constraints violated)
    #[error("Validation failed: mass error = {mass_error:.3e} kg/s, energy error = {energy_error:.3e} W")]
    Validation {
        /// Mass balance error in kg/s
        mass_error: f64,
        /// Energy balance error in W
        energy_error: f64,
    },
}

impl ThermoError {
    /// Creates a new `InvalidInput` error with the given message.
    #[inline]
    pub fn invalid_input(msg: impl Into<String>) -> Self {
        Self::InvalidInput(msg.into())
    }

    /// Creates a new `NotSupported` error with the given message.
    #[inline]
    pub fn not_supported(msg: impl Into<String>) -> Self {
        Self::NotSupported(msg.into())
    }
}

impl From<entropyk_components::ComponentError> for ThermoError {
    #[inline]
    fn from(e: entropyk_components::ComponentError) -> Self {
        Self::Component(e)
    }
}

impl From<entropyk_solver::SolverError> for ThermoError {
    #[inline]
    fn from(e: entropyk_solver::SolverError) -> Self {
        Self::Solver(e)
    }
}

impl From<entropyk_fluids::FluidError> for ThermoError {
    #[inline]
    fn from(e: entropyk_fluids::FluidError) -> Self {
        Self::Fluid(e)
    }
}

impl From<entropyk_solver::TopologyError> for ThermoError {
    #[inline]
    fn from(e: entropyk_solver::TopologyError) -> Self {
        Self::Topology(e)
    }
}

impl From<entropyk_solver::AddEdgeError> for ThermoError {
    #[inline]
    fn from(e: entropyk_solver::AddEdgeError) -> Self {
        Self::AddEdge(e)
    }
}

impl From<entropyk_components::ConnectionError> for ThermoError {
    #[inline]
    fn from(e: entropyk_components::ConnectionError) -> Self {
        Self::Connection(e)
    }
}

impl From<entropyk_solver::ConstraintError> for ThermoError {
    #[inline]
    fn from(e: entropyk_solver::ConstraintError) -> Self {
        Self::Constraint(e)
    }
}

impl From<entropyk_solver::InitializerError> for ThermoError {
    #[inline]
    fn from(e: entropyk_solver::InitializerError) -> Self {
        Self::Initialization(e)
    }
}

impl From<entropyk_core::CalibValidationError> for ThermoError {
    #[inline]
    fn from(e: entropyk_core::CalibValidationError) -> Self {
        Self::Calibration(e)
    }
}

impl From<entropyk_fluids::MixtureError> for ThermoError {
    #[inline]
    fn from(e: entropyk_fluids::MixtureError) -> Self {
        Self::Mixture(e)
    }
}

impl From<SystemBuilderError> for ThermoError {
    #[inline]
    fn from(e: SystemBuilderError) -> Self {
        Self::Builder(e)
    }
}

/// A specialized `Result` type for Entropyk operations.
pub type ThermoResult<T> = Result<T, ThermoError>;
