use napi_derive::napi;
use napi::{bindgen_prelude::*};
use serde_json;
use crate::indicators::{
    SMA, EMA, RSI, MACD, BollingerBands, Stochastic, ATR,
    Indicator, IncrementalIndicator
};

/// Convert JS array to Vec<f64>
fn js_array_to_vec(arr: Vec<f64>) -> Vec<f64> {
    arr
}

#[napi]
pub struct TechnicalIndicators {}

#[napi]
impl TechnicalIndicators {
    #[napi(constructor)]
    pub fn new() -> Self {
        Self {}
    }

    /// Calculate Simple Moving Average
    #[napi]
    pub fn calculate_sma(&self, values: Vec<f64>, period: u32) -> Result<Vec<f64>> {
        match SMA::calculate_series(&values, period as usize) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate Exponential Moving Average
    #[napi]
    pub fn calculate_ema(&self, values: Vec<f64>, period: u32) -> Result<Vec<f64>> {
        match EMA::calculate_series(&values, period as usize) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate Relative Strength Index
    #[napi]
    pub fn calculate_rsi(&self, values: Vec<f64>, period: u32) -> Result<Vec<f64>> {
        match RSI::calculate_series(&values, period as usize) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate MACD - returns JSON string
    #[napi]
    pub fn calculate_macd(
        &self, 
        values: Vec<f64>, 
        fast_period: u32, 
        slow_period: u32, 
        signal_period: u32
    ) -> Result<String> {
        match MACD::calculate_series(&values, fast_period as usize, slow_period as usize, signal_period as usize) {
            Ok((macd, signal, histogram)) => {
                let result = serde_json::json!({
                    "macd": macd,
                    "signal": signal,
                    "histogram": histogram
                });
                Ok(result.to_string())
            }
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate Bollinger Bands - returns JSON string
    #[napi]
    pub fn calculate_bollinger_bands(
        &self, 
        values: Vec<f64>, 
        period: u32, 
        std_dev: f64
    ) -> Result<String> {
        match BollingerBands::calculate_series(&values, period as usize, std_dev) {
            Ok((middle, upper, lower)) => {
                let result = serde_json::json!({
                    "middle": middle,
                    "upper": upper,
                    "lower": lower
                });
                Ok(result.to_string())
            }
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate Stochastic Oscillator - returns JSON string
    #[napi]
    pub fn calculate_stochastic(
        &self,
        high: Vec<f64>,
        low: Vec<f64>,
        close: Vec<f64>,
        k_period: u32,
        d_period: u32,
        smooth_k: u32
    ) -> Result<String> {
        match Stochastic::calculate_series(
            &high, 
            &low, 
            &close, 
            k_period as usize, 
            d_period as usize, 
            smooth_k as usize
        ) {
            Ok((k, d)) => {
                let result = serde_json::json!({
                    "k": k,
                    "d": d
                });
                Ok(result.to_string())
            }
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    /// Calculate Average True Range
    #[napi]
    pub fn calculate_atr(
        &self,
        high: Vec<f64>,
        low: Vec<f64>,
        close: Vec<f64>,
        period: u32
    ) -> Result<Vec<f64>> {
        match ATR::calculate_series(&high, &low, &close, period as usize) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }
}

/// Incremental indicator calculator for streaming data
#[napi]
pub struct IncrementalSMA {
    indicator: SMA,
}

#[napi]
impl IncrementalSMA {
    #[napi(constructor)]
    pub fn new(period: u32) -> Result<Self> {
        match SMA::new(period as usize) {
            Ok(indicator) => Ok(Self { indicator }),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn update(&mut self, value: f64) -> Result<Option<f64>> {
        match self.indicator.update(value) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn current(&self) -> Option<f64> {
        self.indicator.current()
    }

    #[napi]
    pub fn reset(&mut self) {
        self.indicator.reset();
    }

    #[napi]
    pub fn is_ready(&self) -> bool {
        self.indicator.is_ready()
    }
}

/// Incremental EMA calculator
#[napi]
pub struct IncrementalEMA {
    indicator: EMA,
}

#[napi]
impl IncrementalEMA {
    #[napi(constructor)]
    pub fn new(period: u32) -> Result<Self> {
        match EMA::new(period as usize) {
            Ok(indicator) => Ok(Self { indicator }),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn update(&mut self, value: f64) -> Result<Option<f64>> {
        match self.indicator.update(value) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn current(&self) -> Option<f64> {
        self.indicator.current()
    }

    #[napi]
    pub fn reset(&mut self) {
        self.indicator.reset();
    }
}

/// Incremental RSI calculator
#[napi]
pub struct IncrementalRSI {
    indicator: RSI,
}

#[napi]
impl IncrementalRSI {
    #[napi(constructor)]
    pub fn new(period: u32) -> Result<Self> {
        match RSI::new(period as usize) {
            Ok(indicator) => Ok(Self { indicator }),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn update(&mut self, value: f64) -> Result<Option<f64>> {
        match self.indicator.update(value) {
            Ok(result) => Ok(result),
            Err(e) => Err(Error::from_reason(e.to_string())),
        }
    }

    #[napi]
    pub fn current(&self) -> Option<f64> {
        self.indicator.current()
    }
}