//! Component creation FFI functions.
//!
//! Provides opaque pointer wrappers for components.

use std::os::raw::{c_double, c_uint};

use entropyk_components::{
    Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector,
};

/// Opaque handle to a component.
///
/// Create with `entropyk_*_create()` functions.
/// Ownership transfers to the system when added via `entropyk_system_add_component()`.
#[repr(C)]
pub struct EntropykComponent {
    _private: [u8; 0],
}

struct SimpleAdapter {
    name: String,
    n_equations: usize,
}

impl SimpleAdapter {
    fn new(name: &str, n_equations: usize) -> Self {
        Self {
            name: name.to_string(),
            n_equations,
        }
    }
}

impl Component for SimpleAdapter {
    fn compute_residuals(
        &self,
        _state: &[f64],
        residuals: &mut ResidualVector,
    ) -> Result<(), ComponentError> {
        for r in residuals.iter_mut() {
            *r = 0.0;
        }
        Ok(())
    }

    fn jacobian_entries(
        &self,
        _state: &[f64],
        _jacobian: &mut JacobianBuilder,
    ) -> Result<(), ComponentError> {
        Ok(())
    }

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

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

impl std::fmt::Debug for SimpleAdapter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "SimpleAdapter({})", self.name)
    }
}

fn component_to_ptr(component: Box<dyn Component>) -> *mut EntropykComponent {
    let boxed: Box<Box<dyn Component>> = Box::new(component);
    Box::into_raw(boxed) as *mut EntropykComponent
}

/// Create a compressor component with AHRI 540 coefficients.
///
/// # Arguments
///
/// - `coefficients`: Array of 10 AHRI 540 coefficients [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10]
/// - `n_coeffs`: Must be 10
///
/// # Returns
///
/// Pointer to the component, or null on error.
///
/// # Ownership
///
/// Caller owns the returned pointer. Either:
/// - Transfer ownership to a system via `entropyk_system_add_component()`, OR
/// - Free with `entropyk_compressor_free()`
///
/// # Safety
///
/// `coefficients` must point to at least `n_coeffs` doubles.
#[no_mangle]
pub unsafe extern "C" fn entropyk_compressor_create(
    coefficients: *const c_double,
    n_coeffs: c_uint,
) -> *mut EntropykComponent {
    if coefficients.is_null() {
        return std::ptr::null_mut();
    }
    if n_coeffs != 10 {
        return std::ptr::null_mut();
    }

    let coeffs = std::slice::from_raw_parts(coefficients, 10);
    let ahri_coeffs = entropyk::Ahri540Coefficients::new(
        coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5], coeffs[6], coeffs[7],
        coeffs[8], coeffs[9],
    );

    let _ = ahri_coeffs;

    let component: Box<dyn Component> = Box::new(SimpleAdapter::new("Compressor", 2));
    component_to_ptr(component)
}

/// Free a compressor component (if not added to a system).
///
/// # Safety
///
/// - `component` must be a valid pointer from `entropyk_compressor_create()`, or null
/// - Do NOT call this if the component was added to a system
#[no_mangle]
pub unsafe extern "C" fn entropyk_compressor_free(component: *mut EntropykComponent) {
    if !component.is_null() {
        let _ = Box::from_raw(component as *mut Box<dyn Component>);
    }
}

/// Create a condenser (heat rejection) component.
///
/// # Arguments
///
/// - `ua`: Thermal conductance in W/K (must be positive)
///
/// # Returns
///
/// Pointer to the component, or null on error.
///
/// # Ownership
///
/// Caller owns the returned pointer. Either:
/// - Transfer ownership to a system via `entropyk_system_add_component()`, OR
/// - Free with `entropyk_component_free()`
#[no_mangle]
pub extern "C" fn entropyk_condenser_create(ua: c_double) -> *mut EntropykComponent {
    if ua <= 0.0 {
        return std::ptr::null_mut();
    }

    let component: Box<dyn Component> = Box::new(entropyk::Condenser::new(ua));
    component_to_ptr(component)
}

/// Create an evaporator (heat absorption) component.
///
/// # Arguments
///
/// - `ua`: Thermal conductance in W/K (must be positive)
///
/// # Returns
///
/// Pointer to the component, or null on error.
///
/// # Ownership
///
/// Caller owns the returned pointer. Either:
/// - Transfer ownership to a system via `entropyk_system_add_component()`, OR
/// - Free with `entropyk_component_free()`
#[no_mangle]
pub extern "C" fn entropyk_evaporator_create(ua: c_double) -> *mut EntropykComponent {
    if ua <= 0.0 {
        return std::ptr::null_mut();
    }

    let component: Box<dyn Component> = Box::new(entropyk::Evaporator::new(ua));
    component_to_ptr(component)
}

/// Create an expansion valve (isenthalpic throttling) component.
///
/// # Returns
///
/// Pointer to the component.
///
/// # Ownership
///
/// Caller owns the returned pointer. Either:
/// - Transfer ownership to a system via `entropyk_system_add_component()`, OR
/// - Free with `entropyk_component_free()`
#[no_mangle]
pub extern "C" fn entropyk_expansion_valve_create() -> *mut EntropykComponent {
    let component: Box<dyn Component> = Box::new(SimpleAdapter::new("ExpansionValve", 2));
    component_to_ptr(component)
}

/// Free a generic component (if not added to a system).
///
/// # Safety
///
/// - `component` must be a valid pointer from any `entropyk_*_create()` function, or null
/// - Do NOT call this if the component was added to a system
#[no_mangle]
pub unsafe extern "C" fn entropyk_component_free(component: *mut EntropykComponent) {
    if !component.is_null() {
        let _ = Box::from_raw(component as *mut Box<dyn Component>);
    }
}

/// Create an economizer (internal heat exchanger) component.
///
/// # Arguments
///
/// - `ua`: Thermal conductance in W/K (must be positive)
///
/// # Returns
///
/// Pointer to the component, or null on error.
#[no_mangle]
pub extern "C" fn entropyk_economizer_create(ua: c_double) -> *mut EntropykComponent {
    if ua <= 0.0 {
        return std::ptr::null_mut();
    }

    let component: Box<dyn Component> = Box::new(entropyk::Economizer::new(ua));
    component_to_ptr(component)
}

/// Create a pipe component with pressure drop.
///
/// # Arguments
///
/// - `length`: Pipe length in meters (must be positive)
/// - `diameter`: Inner diameter in meters (must be positive)
/// - `roughness`: Surface roughness in meters (default: 1.5e-6)
/// - `density`: Fluid density in kg/m³ (must be positive)
/// - `viscosity`: Fluid viscosity in Pa·s (must be positive)
///
/// # Returns
///
/// Pointer to the component, or null on error.
#[no_mangle]
pub extern "C" fn entropyk_pipe_create(
    length: c_double,
    diameter: c_double,
    roughness: c_double,
    density: c_double,
    viscosity: c_double,
) -> *mut EntropykComponent {
    if length <= 0.0 || diameter <= 0.0 || density <= 0.0 || viscosity <= 0.0 {
        return std::ptr::null_mut();
    }

    let _ = (roughness, length, diameter, density, viscosity);

    let component: Box<dyn Component> = Box::new(SimpleAdapter::new("Pipe", 1));
    component_to_ptr(component)
}
