//! Production Feature Extractor Adapter for Common ML Strategy
//!
//! This adapter bridges the ml::features::extraction::FeatureExtractor with
//! the common::ml_strategy::ProductionFeatureExtractor225 trait to inject
//! 225-feature extraction into SharedMLStrategy without circular dependencies.

use anyhow::Result;
use chrono::{DateTime, Utc};
use common::ml_strategy::ProductionFeatureExtractor225;

use super::extraction::{FeatureExtractor, OHLCVBar};

/// Production-grade 225-feature extractor adapter for SharedMLStrategy
///
/// This struct wraps the ml::features::extraction::FeatureExtractor and implements
/// the ProductionFeatureExtractor225 trait from common, enabling dependency injection
/// of the full 225-feature extractor into SharedMLStrategy.
///
/// # Usage
/// ```rust,ignore
/// use ml::features::ProductionFeatureExtractorAdapter;
/// use common::ml_strategy::SharedMLStrategy;
///
/// let extractor = Box::new(ProductionFeatureExtractorAdapter::new());
/// let strategy = SharedMLStrategy::new(extractor, vec![], 0.7);
/// ```
#[derive(Debug)]
pub struct ProductionFeatureExtractorAdapter {
    inner: FeatureExtractor,
}

impl ProductionFeatureExtractorAdapter {
    /// Create new production feature extractor adapter
    pub fn new() -> Self {
        Self {
            inner: FeatureExtractor::new(),
        }
    }
}

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

impl ProductionFeatureExtractor225 for ProductionFeatureExtractorAdapter {
    fn update(&mut self, price: f64, volume: f64, timestamp: DateTime<Utc>) -> Result<()> {
        // Convert price/volume to OHLCV bar (approximate high/low from price)
        let bar = OHLCVBar {
            timestamp,
            open: price,
            high: price * 1.001, // Approximate high (0.1% above close)
            low: price * 0.999,  // Approximate low (0.1% below close)
            close: price,
            volume,
        };

        // Update internal state
        self.inner.update(&bar)
    }

    fn extract_features(&mut self) -> Result<Vec<f64>> {
        // Extract 225-dimensional feature vector
        let feature_array = self.inner.extract_current_features()?;
        Ok(feature_array.to_vec()) // cpu-side array→vec
    }
}

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

    #[test]
    fn test_adapter_basic_usage() -> Result<()> {
        let mut adapter = ProductionFeatureExtractorAdapter::new();

        // Feed 60 bars (warmup period = 50)
        for i in 0..60 {
            let price = 100.0 + i as f64;
            let volume = 1000.0;
            let timestamp = Utc::now();
            adapter.update(price, volume, timestamp)?;
        }

        // Extract features
        let features = adapter.extract_features()?;

        // Validate 42 features
        assert_eq!(features.len(), 42, "Should extract exactly 42 features");

        // Validate all features are finite
        for (i, &val) in features.iter().enumerate() {
            assert!(
                val.is_finite(),
                "Feature {} should be finite, found {}",
                i, val
            );
        }

        Ok(())
    }

    #[test]
    fn test_adapter_warmup_period() -> Result<()> {
        let mut adapter = ProductionFeatureExtractorAdapter::new();

        // Feed only warmup bars (50)
        for i in 0..50 {
            let price = 100.0 + i as f64;
            let volume = 1000.0;
            let timestamp = Utc::now();
            adapter.update(price, volume, timestamp)?;
        }

        // Should be able to extract features after warmup
        let features = adapter.extract_features()?;
        assert_eq!(features.len(), 42);

        Ok(())
    }
}
