//! OHLCV Bar builder for testing

use chrono::{DateTime, Duration, Utc};
use ml::data_loader::OHLCVBar;

/// Builder for creating OHLCV bars
pub struct BarBuilder {
    timestamp: DateTime<Utc>,
    open: f64,
    high: f64,
    low: f64,
    close: f64,
    volume: f64,
}

impl BarBuilder {
    pub fn new() -> Self {
        let price = 100.0;
        Self {
            timestamp: Utc::now(),
            open: price,
            high: price * 1.01,
            low: price * 0.99,
            close: price,
            volume: 1000.0,
        }
    }

    pub fn timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
        self.timestamp = timestamp;
        self
    }

    pub fn price(mut self, price: f64) -> Self {
        self.open = price;
        self.close = price;
        self.high = price * 1.01;
        self.low = price * 0.99;
        self
    }

    pub fn open(mut self, open: f64) -> Self {
        self.open = open;
        self
    }

    pub fn high(mut self, high: f64) -> Self {
        self.high = high;
        self
    }

    pub fn low(mut self, low: f64) -> Self {
        self.low = low;
        self
    }

    pub fn close(mut self, close: f64) -> Self {
        self.close = close;
        self
    }

    pub fn volume(mut self, volume: f64) -> Self {
        self.volume = volume;
        self
    }

    pub fn bullish(mut self) -> Self {
        self.close = self.open * 1.05;
        self.high = self.close * 1.01;
        self.low = self.open * 0.99;
        self
    }

    pub fn bearish(mut self) -> Self {
        self.close = self.open * 0.95;
        self.high = self.open * 1.01;
        self.low = self.close * 0.99;
        self
    }

    pub fn build(self) -> OHLCVBar {
        OHLCVBar {
            timestamp: self.timestamp,
            open: self.open,
            high: self.high,
            low: self.low,
            close: self.close,
            volume: self.volume,
        }
    }

    /// Build a series of bars with specified spacing
    pub fn build_series(self, count: usize, interval_minutes: i64) -> Vec<OHLCVBar> {
        let mut bars = Vec::with_capacity(count);
        let mut current = self.build();

        for _ in 0..count {
            bars.push(current.clone());
            current.timestamp = current.timestamp + Duration::minutes(interval_minutes);
            // Add small random walk
            current.open = current.close;
            current.close = current.close * (1.0 + (rand::random::<f64>() - 0.5) * 0.02);
            current.high = current.close.max(current.open) * 1.005;
            current.low = current.close.min(current.open) * 0.995;
        }

        bars
    }
}

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

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

    #[test]
    fn test_bar_builder_defaults() {
        let bar = BarBuilder::new().build();
        assert_eq!(bar.open, 100.0);
        assert!(bar.high >= bar.open);
        assert!(bar.low <= bar.open);
    }

    #[test]
    fn test_bullish_bar() {
        let bar = BarBuilder::new().bullish().build();
        assert!(bar.close > bar.open);
        assert_eq!(bar.close / bar.open, 1.05);
    }

    #[test]
    fn test_bearish_bar() {
        let bar = BarBuilder::new().bearish().build();
        assert!(bar.close < bar.open);
        assert_eq!(bar.close / bar.open, 0.95);
    }

    #[test]
    fn test_build_series() {
        let bars = BarBuilder::new().build_series(10, 5);
        assert_eq!(bars.len(), 10);

        // Check timestamps are 5 minutes apart
        for i in 0..bars.len() - 1 {
            let diff = bars[i + 1].timestamp - bars[i].timestamp;
            assert_eq!(diff.num_minutes(), 5);
        }
    }
}
