//! Repository traits for clean database abstraction in backtesting service

use anyhow::Result;
use async_trait::async_trait;
use chrono::{DateTime, Utc};

use crate::foxhunt::tli::BacktestStatus;
use crate::performance::PerformanceMetrics;
use crate::storage::BacktestSummary;
use crate::strategy_engine::BacktestTrade;

/// Repository trait for market data operations
///
/// This trait abstracts market data retrieval for backtesting,
/// eliminating direct database coupling from business logic.
#[async_trait]
pub trait MarketDataRepository: Send + Sync {
    /// Load historical market data for backtesting
    ///
    /// # Arguments
    /// * `symbols` - List of symbols to load data for
    ///
    /// * `start_time` - Start timestamp in nanoseconds
    /// * `end_time` - End timestamp in nanoseconds
    ///
    /// # Returns
    ///
    /// Vector of market data events sorted by timestamp
    async fn load_historical_data(
        &self,
        symbols: &[String],
        start_time: i64,
        end_time: i64,
    ) -> Result<Vec<crate::strategy_engine::MarketData>>;
}

/// Repository trait for trading and backtest result operations
///
/// This trait handles persistence and retrieval of backtest results,
/// trading history, and performance metrics.
#[async_trait]
pub trait TradingRepository: Send + Sync {
    /// Save backtest results to storage
    async fn save_backtest_results(
        &self,
        backtest_id: &str,
        trades: &[BacktestTrade],
        metrics: &PerformanceMetrics,
    ) -> Result<()>;

    /// Load backtest results from storage
    async fn load_backtest_results(
        &self,
        backtest_id: &str,
    ) -> Result<(Vec<BacktestTrade>, PerformanceMetrics)>;

    /// List historical backtests
    async fn list_backtests(
        &self,
        limit: u32,
        offset: u32,
        strategy_name: Option<String>,
        status_filter: Option<BacktestStatus>,
    ) -> Result<Vec<BacktestSummary>>;
}

/// Repository trait for news and sentiment data
///
/// This trait provides access to news events and sentiment data
/// that can influence trading strategies.
#[async_trait]
pub trait NewsRepository: Send + Sync {
    /// Load news events for given symbols and time range
    async fn load_news_events(
        &self,
        symbols: &[String],
        start_time: DateTime<Utc>,
        end_time: DateTime<Utc>,
    ) -> Result<Vec<crate::strategy_engine::NewsEvent>>;
}

/// Combined repository trait for dependency injection
///
/// This trait combines all repository interfaces to simplify
/// dependency injection in the service layer.
#[async_trait]
pub trait BacktestingRepositories: Send + Sync {
    /// Get market data repository
    fn market_data(&self) -> &dyn MarketDataRepository;

    /// Get trading repository
    fn trading(&self) -> &dyn TradingRepository;

    /// Get news repository
    fn news(&self) -> &dyn NewsRepository;
}

/// Default implementation that provides all repositories
pub struct DefaultRepositories {
    /// Market data repository for historical data
    pub market_data: Box<dyn MarketDataRepository>,
    /// Trading repository for order and execution data
    pub trading: Box<dyn TradingRepository>,
    /// News repository for market news events
    pub news: Box<dyn NewsRepository>,
}

#[async_trait]
impl BacktestingRepositories for DefaultRepositories {
    fn market_data(&self) -> &dyn MarketDataRepository {
        self.market_data.as_ref()
    }

    fn trading(&self) -> &dyn TradingRepository {
        self.trading.as_ref()
    }

    fn news(&self) -> &dyn NewsRepository {
        self.news.as_ref()
    }
}

#[cfg(any(test, feature = "test-utils"))]
impl DefaultRepositories {
    /// Create a mock instance for testing
    ///
    /// This method is only available in test builds and creates
    /// in-memory mock repositories for unit testing.
    pub fn mock() -> Self {
        use std::sync::Arc;
        use tokio::sync::RwLock;
        use std::collections::HashMap;

        // Mock market data repository
        struct MockMarketData {
            data: Arc<RwLock<Vec<crate::strategy_engine::MarketData>>>,
        }

        #[async_trait]
        impl MarketDataRepository for MockMarketData {
            async fn load_historical_data(
                &self,
                _symbols: &[String],
                _start_time: i64,
                _end_time: i64,
            ) -> Result<Vec<crate::strategy_engine::MarketData>> {
                Ok(self.data.read().await.clone())
            }
        }

        // Mock trading repository
        struct MockTrading {
            trades: Arc<RwLock<HashMap<String, Vec<BacktestTrade>>>>,
            metrics: Arc<RwLock<HashMap<String, PerformanceMetrics>>>,
            backtests: Arc<RwLock<Vec<BacktestSummary>>>,
        }

        #[async_trait]
        impl TradingRepository for MockTrading {
            async fn save_backtest_results(
                &self,
                backtest_id: &str,
                trades: &[BacktestTrade],
                metrics: &PerformanceMetrics,
            ) -> Result<()> {
                self.trades.write().await.insert(backtest_id.to_string(), trades.to_vec());
                self.metrics.write().await.insert(backtest_id.to_string(), metrics.clone());
                Ok(())
            }

            async fn load_backtest_results(
                &self,
                backtest_id: &str,
            ) -> Result<(Vec<BacktestTrade>, PerformanceMetrics)> {
                let trades = self.trades.read().await
                    .get(backtest_id)
                    .cloned()
                    .unwrap_or_default();
                let metrics = self.metrics.read().await
                    .get(backtest_id)
                    .cloned()
                    .unwrap_or_default();
                Ok((trades, metrics))
            }

            async fn list_backtests(
                &self,
                limit: u32,
                offset: u32,
                strategy_name: Option<String>,
                status_filter: Option<BacktestStatus>,
            ) -> Result<Vec<BacktestSummary>> {
                let backtests = self.backtests.read().await;
                let filtered: Vec<BacktestSummary> = backtests
                    .iter()
                    .filter(|bt| {
                        let name_match = strategy_name.as_ref()
                            .map(|n| bt.strategy_name == *n)
                            .unwrap_or(true);
                        let status_match = status_filter.map(|s| bt.status == s).unwrap_or(true);
                        name_match && status_match
                    })
                    .skip(offset as usize)
                    .take(limit as usize)
                    .cloned()
                    .collect();
                Ok(filtered)
            }
        }

        // Mock news repository
        struct MockNews {
            events: Arc<RwLock<Vec<crate::strategy_engine::NewsEvent>>>,
        }

        #[async_trait]
        impl NewsRepository for MockNews {
            async fn load_news_events(
                &self,
                _symbols: &[String],
                _start_time: DateTime<Utc>,
                _end_time: DateTime<Utc>,
            ) -> Result<Vec<crate::strategy_engine::NewsEvent>> {
                Ok(self.events.read().await.clone())
            }
        }

        Self {
            market_data: Box::new(MockMarketData {
                data: Arc::new(RwLock::new(Vec::new())),
            }),
            trading: Box::new(MockTrading {
                trades: Arc::new(RwLock::new(HashMap::new())),
                metrics: Arc::new(RwLock::new(HashMap::new())),
                backtests: Arc::new(RwLock::new(Vec::new())),
            }),
            news: Box::new(MockNews {
                events: Arc::new(RwLock::new(Vec::new())),
            }),
        }
    }
}
