//! WASM solver bindings.
//!
//! Provides JavaScript-friendly wrappers for the solver and system.

use crate::backend::create_default_backend;
use entropyk_solver::{
    ConvergedState, FallbackConfig, FallbackSolver, NewtonConfig, PicardConfig, Solver,
    SolverStrategy, System,
};
use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;

/// WASM wrapper for Newton-Raphson solver configuration.
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct WasmNewtonConfig {
    pub(crate) inner: NewtonConfig,
}

#[wasm_bindgen]
impl WasmNewtonConfig {
    /// Create default Newton-Raphson configuration.
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        WasmNewtonConfig {
            inner: NewtonConfig::default(),
        }
    }

    /// Set maximum iterations.
    pub fn set_max_iterations(&mut self, max: usize) {
        self.inner.max_iterations = max;
    }

    /// Set convergence tolerance.
    pub fn set_tolerance(&mut self, tol: f64) {
        self.inner.tolerance = tol;
    }
}

impl Default for WasmNewtonConfig {
    fn default() -> Self {
        Self::new()
    }
}

/// WASM wrapper for Picard (Sequential Substitution) solver configuration.
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct WasmPicardConfig {
    pub(crate) inner: PicardConfig,
}

#[wasm_bindgen]
impl WasmPicardConfig {
    /// Create default Picard configuration.
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        WasmPicardConfig {
            inner: PicardConfig::default(),
        }
    }

    /// Set maximum iterations.
    pub fn set_max_iterations(&mut self, max: usize) {
        self.inner.max_iterations = max;
    }

    /// Set relaxation factor.
    pub fn set_relaxation_factor(&mut self, omega: f64) {
        self.inner.relaxation_factor = omega;
    }
}

impl Default for WasmPicardConfig {
    fn default() -> Self {
        Self::new()
    }
}

/// WASM wrapper for fallback solver configuration.
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct WasmFallbackConfig {
    newton_config: NewtonConfig,
    picard_config: PicardConfig,
}

#[wasm_bindgen]
impl WasmFallbackConfig {
    /// Create default fallback configuration.
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        WasmFallbackConfig {
            newton_config: NewtonConfig::default(),
            picard_config: PicardConfig::default(),
        }
    }
}

impl Default for WasmFallbackConfig {
    fn default() -> Self {
        Self::new()
    }
}

/// WASM wrapper for converged state (solver result).
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct WasmConvergedState {
    /// Convergence status
    pub converged: bool,
    /// Number of iterations
    pub iterations: usize,
    /// Final residual
    pub final_residual: f64,
}

#[wasm_bindgen]
impl WasmConvergedState {
    /// Convert to JSON string.
    pub fn toJson(&self) -> String {
        format!(
            r#"{{"converged":{},"iterations":{},"final_residual":{}}}"#,
            self.converged, self.iterations, self.final_residual
        )
    }
}

impl From<&ConvergedState> for WasmConvergedState {
    fn from(state: &ConvergedState) -> Self {
        WasmConvergedState {
            converged: state.is_converged(),
            iterations: state.iterations,
            final_residual: state.final_residual,
        }
    }
}

/// WASM wrapper for System (thermodynamic system).
#[wasm_bindgen]
pub struct WasmSystem {
    inner: Rc<RefCell<System>>,
}

#[wasm_bindgen]
impl WasmSystem {
    /// Create a new thermodynamic system.
    #[wasm_bindgen(constructor)]
    pub fn new() -> Result<WasmSystem, JsValue> {
        let system = System::new();
        Ok(WasmSystem {
            inner: Rc::new(RefCell::new(system)),
        })
    }

    /// Solve the system with fallback strategy.
    pub fn solve(&mut self, _config: WasmFallbackConfig) -> Result<WasmConvergedState, JsValue> {
        let mut solver = FallbackSolver::default();
        let state = solver
            .solve(&mut self.inner.borrow_mut())
            .map_err(|e: entropyk_solver::SolverError| js_sys::Error::new(&e.to_string()))?;

        Ok((&state).into())
    }

    /// Solve with Newton-Raphson method.
    pub fn solve_newton(
        &mut self,
        config: WasmNewtonConfig,
    ) -> Result<WasmConvergedState, JsValue> {
        let mut solver = SolverStrategy::NewtonRaphson(config.inner);
        let state = solver
            .solve(&mut self.inner.borrow_mut())
            .map_err(|e: entropyk_solver::SolverError| js_sys::Error::new(&e.to_string()))?;

        Ok((&state).into())
    }

    /// Solve with Picard (Sequential Substitution) method.
    pub fn solve_picard(
        &mut self,
        config: WasmPicardConfig,
    ) -> Result<WasmConvergedState, JsValue> {
        let mut solver = SolverStrategy::SequentialSubstitution(config.inner);
        let state = solver
            .solve(&mut self.inner.borrow_mut())
            .map_err(|e: entropyk_solver::SolverError| js_sys::Error::new(&e.to_string()))?;

        Ok((&state).into())
    }

    /// Solve with Picard (Sequential Substitution) method.
    pub fn solve_picard(
        &mut self,
        config: WasmPicardConfig,
    ) -> Result<WasmConvergedState, JsValue> {
        let mut solver = config.inner;
        let state = solver
            .solve(&mut self.inner.borrow_mut())
            .map_err(|e| js_sys::Error::new(&e.to_string()))?;

        Ok((&state).into())
    }

    /// Get node count.
    pub fn node_count(&self) -> usize {
        self.inner.borrow().node_count()
    }

    /// Get edge count.
    pub fn edge_count(&self) -> usize {
        self.inner.borrow().edge_count()
    }

    /// Convert system state to JSON.
    pub fn toJson(&self) -> String {
        format!(
            r#"{{"node_count":{},"edge_count":{}}}"#,
            self.node_count(),
            self.edge_count()
        )
    }
}

impl Default for WasmSystem {
    fn default() -> Self {
        Self::new().expect("Failed to create default system")
    }
}

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

    #[wasm_bindgen_test]
    fn test_newton_config_creation() {
        let config = WasmNewtonConfig::new();
        assert!(config.inner.max_iterations > 0);
    }

    #[wasm_bindgen_test]
    fn test_picard_config_creation() {
        let config = WasmPicardConfig::new();
        assert!(config.inner.max_iterations > 0);
    }

    #[wasm_bindgen_test]
    fn test_fallback_config_creation() {
        let config = WasmFallbackConfig::new();
        assert!(config.newton_config.max_iterations > 0);
    }

    #[wasm_bindgen_test]
    fn test_system_creation() {
        let system = WasmSystem::new();
        assert!(system.is_ok());
    }
}
