//! Execution repository implementation
//!
//! This module provides repository pattern implementation for trade executions
//! with `PostgreSQL` backing. It includes comprehensive trade history tracking,
//! execution reporting, and performance analytics for high-frequency trading.

use async_trait::async_trait;
use chrono::{DateTime, NaiveDate, Utc};
use rust_decimal::Decimal;
// Removed direct rust_decimal import - using common::Decimal via common crate
use sqlx::{Pool, Postgres, Row};
use uuid::Uuid;

use crate::{Repository, Result};

// Import trading types directly from common
use common::types::{Execution, OrderSide};

/// Filter criteria for execution queries
#[derive(Debug, Default, Clone)]
pub struct ExecutionFilter {
    /// Filter by trading symbol
    pub symbol: Option<String>,

    /// Filter by related order ID
    pub order_id: Option<Uuid>,

    /// Filter by execution side
    pub side: Option<OrderSide>,

    /// Filter executions with quantity greater than this value
    pub min_quantity: Option<Decimal>,

    /// Filter executions with quantity less than this value
    pub max_quantity: Option<Decimal>,

    /// Filter executions with price greater than this value
    pub min_price: Option<Decimal>,

    /// Filter executions with price less than this value
    pub max_price: Option<Decimal>,

    /// Filter by broker execution ID
    pub broker_execution_id: Option<String>,

    /// Filter by trading venue
    pub venue: Option<String>,

    /// Filter by counterparty
    pub counterparty: Option<String>,

    /// Filter executions after this timestamp
    pub executed_after: Option<DateTime<Utc>>,

    /// Filter executions before this timestamp
    pub executed_before: Option<DateTime<Utc>>,

    /// Filter by minimum gross value
    pub min_gross_value: Option<Decimal>,

    /// Filter by maximum gross value
    pub max_gross_value: Option<Decimal>,

    /// Limit number of results
    pub limit: Option<i64>,

    /// Offset for pagination
    pub offset: Option<i64>,
}

impl ExecutionFilter {
    /// Create a new empty filter
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Filter by symbol
    #[must_use]
    pub fn symbol<S: Into<String>>(mut self, symbol: S) -> Self {
        self.symbol = Some(symbol.into());
        self
    }

    /// Filter by order ID
    #[must_use]
    pub fn order_id(mut self, order_id: Uuid) -> Self {
        self.order_id = Some(order_id);
        self
    }

    /// Filter by execution side
    #[must_use]
    pub fn side(mut self, side: OrderSide) -> Self {
        self.side = Some(side);
        self
    }

    /// Filter by quantity range
    #[must_use]
    pub fn quantity_range(mut self, min: Option<Decimal>, max: Option<Decimal>) -> Self {
        self.min_quantity = min;
        self.max_quantity = max;
        self
    }

    /// Filter by price range
    #[must_use]
    pub fn price_range(mut self, min: Option<Decimal>, max: Option<Decimal>) -> Self {
        self.min_price = min;
        self.max_price = max;
        self
    }

    /// Filter by value range
    #[must_use]
    pub fn value_range(mut self, min: Option<Decimal>, max: Option<Decimal>) -> Self {
        self.min_gross_value = min;
        self.max_gross_value = max;
        self
    }

    /// Filter by venue
    #[must_use]
    pub fn venue<S: Into<String>>(mut self, venue: S) -> Self {
        self.venue = Some(venue.into());
        self
    }

    /// Filter by counterparty
    #[must_use]
    pub fn counterparty<S: Into<String>>(mut self, counterparty: S) -> Self {
        self.counterparty = Some(counterparty.into());
        self
    }

    /// Filter by execution time range
    #[must_use]
    pub fn executed_between(mut self, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
        self.executed_after = Some(start);
        self.executed_before = Some(end);
        self
    }

    /// Filter by today's executions
    #[must_use]
    pub fn today(mut self) -> Self {
        let now = Utc::now();
        // and_hms_opt(0, 0, 0) is always valid for midnight
        let start_of_day = now
            .date_naive()
            .and_hms_opt(0, 0, 0)
            .unwrap_or_default()
            .and_utc();
        self.executed_after = Some(start_of_day);
        self.executed_before = Some(now);
        self
    }

    /// Limit results
    #[must_use]
    pub fn limit(mut self, limit: i64) -> Self {
        self.limit = Some(limit);
        self
    }

    /// Set offset for pagination
    #[must_use]
    pub fn offset(mut self, offset: i64) -> Self {
        self.offset = Some(offset);
        self
    }
}

/// Execution repository trait defining available operations
#[async_trait]
pub trait ExecutionRepository: Repository<Execution, Uuid> {
    /// Find executions matching the filter criteria
    async fn find_by_filter(&self, filter: &ExecutionFilter) -> Result<Vec<Execution>>;

    /// Find executions by symbol
    async fn find_by_symbol(&self, symbol: &str) -> Result<Vec<Execution>>;

    /// Find executions by order ID
    async fn find_by_order_id(&self, order_id: &Uuid) -> Result<Vec<Execution>>;

    /// Find executions by side (buy/sell)
    async fn find_by_side(&self, side: OrderSide) -> Result<Vec<Execution>>;

    /// Find executions within a time range
    async fn find_by_time_range(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Result<Vec<Execution>>;

    /// Find executions by venue
    async fn find_by_venue(&self, venue: &str) -> Result<Vec<Execution>>;

    /// Batch insert executions for high-frequency operations
    async fn batch_insert(&self, executions: &[Execution]) -> Result<Vec<Execution>>;

    /// Get execution statistics
    async fn get_execution_stats(
        &self,
        symbol: Option<&str>,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<ExecutionStats>;

    /// Get trading performance metrics
    async fn get_trading_performance(
        &self,
        symbol: Option<&str>,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<TradingPerformance>;

    /// Find large executions (above threshold)
    async fn find_large_executions(&self, value_threshold: Decimal) -> Result<Vec<Execution>>;

    /// Get volume-weighted average price (VWAP) for a symbol
    async fn calculate_vwap(
        &self,
        symbol: &str,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<Decimal>;

    /// Get execution summary by hour for analytics
    async fn get_hourly_execution_summary(
        &self,
        date: NaiveDate,
    ) -> Result<Vec<HourlyExecutionSummary>>;

    /// Find executions with high slippage
    async fn find_high_slippage_executions(
        &self,
        slippage_threshold: Decimal,
    ) -> Result<Vec<ExecutionWithSlippage>>;
}

/// Execution statistics for trading performance analysis
#[derive(Debug, Clone)]
pub struct ExecutionStats {
    /// Total number of executions
    pub total_executions: i64,
    /// Number of buy-side executions
    pub buy_executions: i64,
    /// Number of sell-side executions
    pub sell_executions: i64,
    /// Total trading volume across all executions
    pub total_volume: Decimal,
    /// Total notional value of all executions
    pub total_value: Decimal,
    /// Total fees and commissions paid
    pub total_fees: Decimal,
    /// Average execution size
    pub avg_execution_size: Decimal,
    /// Volume-weighted average price
    pub avg_price: Decimal,
    /// Largest single execution by quantity
    pub largest_execution: Option<Decimal>,
    /// Smallest single execution by quantity
    pub smallest_execution: Option<Decimal>,
    /// Number of unique symbols traded
    pub unique_symbols: i64,
    /// Number of unique trading venues used
    pub unique_venues: i64,
}

/// Trading performance metrics for strategy evaluation
#[derive(Debug, Clone)]
pub struct TradingPerformance {
    /// Total number of completed trades
    pub total_trades: i64,
    /// Number of profitable trades
    pub winning_trades: i64,
    /// Number of unprofitable trades
    pub losing_trades: i64,
    /// Percentage of winning trades
    pub win_rate: Decimal,
    /// Average profit per winning trade
    pub avg_win: Decimal,
    /// Average loss per losing trade
    pub avg_loss: Decimal,
    /// Ratio of gross profit to gross loss
    pub profit_factor: Decimal,
    /// Total profit and loss
    pub total_pnl: Decimal,
    /// Total profit from winning trades
    pub gross_profit: Decimal,
    /// Total loss from losing trades
    pub gross_loss: Decimal,
    /// Largest single winning trade
    pub largest_win: Decimal,
    /// Largest single losing trade
    pub largest_loss: Decimal,
    /// Average trade duration in seconds
    pub avg_trade_duration: Option<i64>,
}

/// Hourly execution summary for time-based analytics
#[derive(Debug, Clone)]
pub struct HourlyExecutionSummary {
    /// Hour of the day (0-23)
    pub hour: i32,
    /// Number of executions in this hour
    pub execution_count: i64,
    /// Total trading volume for this hour
    pub total_volume: Decimal,
    /// Total notional value for this hour
    pub total_value: Decimal,
    /// Volume-weighted average price for this hour
    pub avg_price: Decimal,
    /// Number of unique symbols traded in this hour
    pub unique_symbols: i64,
}

/// Execution with calculated slippage metrics
#[derive(Debug, Clone)]
pub struct ExecutionWithSlippage {
    /// The original execution record
    pub execution: Execution,
    /// Expected price based on reference (e.g., mid-market)
    pub expected_price: Decimal,
    /// Absolute slippage (executed price - expected price)
    pub slippage: Decimal,
    /// Slippage expressed in basis points
    pub slippage_bps: Decimal,
}

/// `PostgreSQL` implementation of `ExecutionRepository`
pub struct PostgresExecutionRepository {
    pool: Pool<Postgres>,
}

impl PostgresExecutionRepository {
    /// Create a new `PostgreSQL` execution repository
    #[must_use]
    pub fn new(pool: Pool<Postgres>) -> Self {
        Self { pool }
    }
}

#[async_trait]
impl Repository<Execution, Uuid> for PostgresExecutionRepository {
    async fn find_by_id(&self, id: &Uuid) -> Result<Option<Execution>> {
        let query = r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue,
                   gross_value, net_value
            FROM executions WHERE id = $1
        ";

        let row = sqlx::query(query)
            .bind(id)
            .fetch_optional(&self.pool)
            .await?;

        match row {
            Some(row) => {
                let execution = Execution {
                    id: row.get("id"),
                    order_id: row.get("order_id"),
                    symbol: row.get("symbol"),
                    quantity: row.get("quantity"),
                    price: row.get("price"),
                    side: row.get("side"),
                    fees: row.get("fees"),
                    fee_currency: row.get("fee_currency"),
                    executed_at: row.get("executed_at"),
                    timestamp: row.get("timestamp"),
                    symbol_hash: row.get("symbol_hash"),
                    broker_execution_id: row.get("broker_execution_id"),
                    counterparty: row.get("counterparty"),
                    venue: row.get("venue"),
                    gross_value: row.get("gross_value"),
                    net_value: row.get("net_value"),
                };
                Ok(Some(execution))
            },
            None => Ok(None),
        }
    }

    async fn save(&self, execution: &Execution) -> Result<Execution> {
        let query = r"
            INSERT INTO executions (id, order_id, symbol, quantity, price, side, fees, fee_currency,
                                  executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue,
                                  gross_value, net_value)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
            ON CONFLICT (id) DO UPDATE SET
                quantity = EXCLUDED.quantity,
                price = EXCLUDED.price,
                fees = EXCLUDED.fees,
                executed_at = EXCLUDED.executed_at,
                timestamp = EXCLUDED.timestamp
            RETURNING *
        ";

        let row = sqlx::query(query)
            .bind(execution.id)
            .bind(execution.order_id)
            .bind(&execution.symbol)
            .bind(execution.quantity)
            .bind(execution.price)
            .bind(execution.side)
            .bind(execution.fees)
            .bind(&execution.fee_currency)
            .bind(execution.executed_at)
            .bind(execution.timestamp)
            .bind(execution.symbol_hash)
            .bind(&execution.broker_execution_id)
            .bind(&execution.counterparty)
            .bind(&execution.venue)
            .bind(execution.gross_value)
            .bind(execution.net_value)
            .fetch_one(&self.pool)
            .await?;

        Ok(Execution {
            id: row.get("id"),
            order_id: row.get("order_id"),
            symbol: row.get("symbol"),
            quantity: row.get("quantity"),
            price: row.get("price"),
            side: row.get("side"),
            fees: row.get("fees"),
            fee_currency: row.get("fee_currency"),
            executed_at: row.get("executed_at"),
            timestamp: row.get("timestamp"),
            symbol_hash: row.get("symbol_hash"),
            broker_execution_id: row.get("broker_execution_id"),
            counterparty: row.get("counterparty"),
            venue: row.get("venue"),
            gross_value: row.get("gross_value"),
            net_value: row.get("net_value"),
        })
    }

    async fn delete(&self, id: &Uuid) -> Result<bool> {
        let result = sqlx::query("DELETE FROM executions WHERE id = $1")
            .bind(id)
            .execute(&self.pool)
            .await?;

        Ok(result.rows_affected() > 0)
    }

    async fn exists(&self, id: &Uuid) -> Result<bool> {
        let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM executions WHERE id = $1")
            .bind(id)
            .fetch_one(&self.pool)
            .await?;

        Ok(count > 0)
    }
}

#[async_trait]
impl ExecutionRepository for PostgresExecutionRepository {
    async fn find_by_filter(&self, filter: &ExecutionFilter) -> Result<Vec<Execution>> {
        let mut conditions = Vec::new();
        let mut query = r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue,
                   gross_value, net_value
            FROM executions
        "
        .to_string();

        // Build dynamic WHERE clause based on filter
        if filter.symbol.is_some() {
            conditions.push("symbol = $1".to_owned());
        }
        // Add other conditions as needed (simplified for brevity)

        if !conditions.is_empty() {
            use std::fmt::Write;
            // Writing to a String never fails
            let _ = write!(query, " WHERE {}", conditions.join(" AND "));
        }

        query.push_str(" ORDER BY execution_timestamp DESC");

        if let Some(limit) = filter.limit {
            use std::fmt::Write;
            // Writing to a String never fails
            let _ = write!(query, " LIMIT {limit}");
        }

        if let Some(offset) = filter.offset {
            use std::fmt::Write;
            // Writing to a String never fails
            let _ = write!(query, " OFFSET {offset}");
        }

        let executions = sqlx::query_as::<_, Execution>(&query)
            .fetch_all(&self.pool)
            .await?;

        Ok(executions)
    }

    async fn find_by_symbol(&self, symbol: &str) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue
            FROM executions WHERE symbol = $1 ORDER BY executed_at DESC
            ",
        )
        .bind(symbol)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn find_by_order_id(&self, order_id: &Uuid) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue
            FROM executions WHERE order_id = $1 ORDER BY executed_at ASC
            ",
        )
        .bind(order_id)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn find_by_side(&self, side: OrderSide) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue
            FROM executions WHERE side = $1 ORDER BY executed_at DESC
            ",
        )
        .bind(side)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn find_by_time_range(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, execution_id, trade_id, symbol, side, quantity, price,
                   commission, commission_currency, sec_fee, taf_fee, clearing_fee,
                   venue, execution_timestamp, settlement_date, is_maker, liquidity_flag,
                   contra_broker, contra_trader, received_at, processed_at, reported_at,
                   execution_details
            FROM fills 
            WHERE execution_timestamp >= $1 AND execution_timestamp <= $2 
            ORDER BY execution_timestamp ASC
            ",
        )
        .bind(start)
        .bind(end)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn find_by_venue(&self, venue: &str) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, symbol, quantity, price, side, fees, fee_currency,
                   executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue
            FROM executions WHERE venue = $1 ORDER BY executed_at DESC
            ",
        )
        .bind(venue)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn batch_insert(&self, executions: &[Execution]) -> Result<Vec<Execution>> {
        if executions.is_empty() {
            return Ok(Vec::new());
        }

        let mut tx = self.pool.begin().await?;
        let mut inserted_executions = Vec::new();

        for execution in executions {
            let query = r"
                INSERT INTO executions (id, order_id, symbol, quantity, price, side, fees, fee_currency,
                                      executed_at, timestamp, symbol_hash, broker_execution_id, counterparty, venue)
                VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
                RETURNING *
            ";

            let row = sqlx::query(query)
                .bind(execution.id)
                .bind(execution.order_id)
                .bind(&execution.symbol)
                .bind(execution.quantity)
                .bind(execution.price)
                .bind(execution.side)
                .bind(execution.fees)
                .bind(&execution.fee_currency)
                .bind(execution.executed_at)
                .bind(execution.timestamp)
                .bind(execution.symbol_hash)
                .bind(&execution.broker_execution_id)
                .bind(&execution.counterparty)
                .bind(&execution.venue)
                .bind(execution.gross_value)
                .bind(execution.net_value)
                .fetch_one(&mut *tx)
                .await?;

            let inserted_execution = Execution {
                id: row.get("id"),
                order_id: row.get("order_id"),
                symbol: row.get("symbol"),
                quantity: row.get("quantity"),
                price: row.get("price"),
                side: row.get("side"),
                fees: row.get("fees"),
                fee_currency: row.get("fee_currency"),
                executed_at: row.get("executed_at"),
                timestamp: row.get("timestamp"),
                symbol_hash: row.get("symbol_hash"),
                broker_execution_id: row.get("broker_execution_id"),
                counterparty: row.get("counterparty"),
                venue: row.get("venue"),
                gross_value: row.get("gross_value"),
                net_value: row.get("net_value"),
            };

            inserted_executions.push(inserted_execution);
        }

        tx.commit().await?;
        Ok(inserted_executions)
    }

    async fn get_execution_stats(
        &self,
        symbol: Option<&str>,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<ExecutionStats> {
        let mut conditions = Vec::new();
        let mut params: Vec<Box<dyn sqlx::Encode<Postgres> + Send>> = Vec::new();
        let mut param_count = 0;

        if let Some(symbol) = symbol {
            param_count += 1;
            conditions.push(format!("symbol = ${param_count}"));
            params.push(Box::new(symbol.to_string()));
        }

        if let Some((start, end)) = time_range {
            param_count += 1;
            conditions.push(format!("executed_at >= ${param_count}"));
            params.push(Box::new(start));

            param_count += 1;
            conditions.push(format!("executed_at <= ${param_count}"));
            params.push(Box::new(end));
        }

        let where_clause = if conditions.is_empty() {
            String::new()
        } else {
            format!("WHERE {}", conditions.join(" AND "))
        };

        let query = format!(
            r"
            SELECT 
                COUNT(*) as total_executions,
                COUNT(CASE WHEN side = 'buy' THEN 1 END) as buy_executions,
                COUNT(CASE WHEN side = 'sell' THEN 1 END) as sell_executions,
                COALESCE(SUM(quantity), 0) as total_volume,
                COALESCE(SUM(quantity * price), 0) as total_value,
                COALESCE(SUM(commission), 0) as total_fees,
                COALESCE(AVG(quantity), 0) as avg_execution_size,
                COALESCE(AVG(price), 0) as avg_price,
                MAX(quantity) as largest_execution,
                MIN(quantity) as smallest_execution,
                COUNT(DISTINCT symbol) as unique_symbols,
                COUNT(DISTINCT venue) as unique_venues
            FROM fills {where_clause}
            "
        );

        let row = sqlx::query(&query).fetch_one(&self.pool).await?;

        Ok(ExecutionStats {
            total_executions: row.get("total_executions"),
            buy_executions: row.get("buy_executions"),
            sell_executions: row.get("sell_executions"),
            total_volume: row.get("total_volume"),
            total_value: row.get("total_value"),
            total_fees: row.get("total_fees"),
            avg_execution_size: row.get("avg_execution_size"),
            avg_price: row.get("avg_price"),
            largest_execution: row.get("largest_execution"),
            smallest_execution: row.get("smallest_execution"),
            unique_symbols: row.get("unique_symbols"),
            unique_venues: row.get("unique_venues"),
        })
    }

    async fn get_trading_performance(
        &self,
        symbol: Option<&str>,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<TradingPerformance> {
        // This is a simplified implementation
        // In practice, you'd need more complex logic to calculate trading performance metrics
        let stats = self.get_execution_stats(symbol, time_range).await?;

        // Simplified calculations (real implementation would be more complex)
        let win_rate = if stats.total_executions > 0 {
            Decimal::from(stats.buy_executions) / Decimal::from(stats.total_executions)
                * Decimal::from(100)
        } else {
            Decimal::ZERO
        };

        Ok(TradingPerformance {
            total_trades: stats.total_executions,
            winning_trades: stats.buy_executions, // Simplified
            losing_trades: stats.sell_executions, // Simplified
            win_rate,
            avg_win: Decimal::ZERO,      // Would need P&L calculation
            avg_loss: Decimal::ZERO,     // Would need P&L calculation
            profit_factor: Decimal::ONE, // Would need P&L calculation
            total_pnl: Decimal::ZERO,    // Would need P&L calculation
            gross_profit: Decimal::ZERO, // Would need P&L calculation
            gross_loss: Decimal::ZERO,   // Would need P&L calculation
            largest_win: Decimal::ZERO,  // Would need P&L calculation
            largest_loss: Decimal::ZERO, // Would need P&L calculation
            avg_trade_duration: None,    // Would need order matching
        })
    }

    async fn find_large_executions(&self, value_threshold: Decimal) -> Result<Vec<Execution>> {
        let executions = sqlx::query_as::<_, Execution>(
            r"
            SELECT id, order_id, execution_id, trade_id, symbol, side, quantity, price,
                   commission, commission_currency, sec_fee, taf_fee, clearing_fee,
                   venue, execution_timestamp, settlement_date, is_maker, liquidity_flag,
                   contra_broker, contra_trader, received_at, processed_at, reported_at,
                   execution_details
            FROM fills 
            WHERE (quantity * price) >= $1 
            ORDER BY (quantity * price) DESC
            ",
        )
        .bind(value_threshold)
        .fetch_all(&self.pool)
        .await?;

        Ok(executions)
    }

    async fn calculate_vwap(
        &self,
        symbol: &str,
        time_range: Option<(DateTime<Utc>, DateTime<Utc>)>,
    ) -> Result<Decimal> {
        let (query, start, end) = if let Some((start, end)) = time_range {
            (
                r"
                SELECT 
                        COALESCE(SUM(quantity * price) / NULLIF(SUM(quantity), 0), 0) as vwap
                    FROM fills 
                    WHERE symbol = $1 AND execution_timestamp >= $2 AND execution_timestamp <= $3
                ",
                Some(start),
                Some(end),
            )
        } else {
            (
                r"
                SELECT 
                        COALESCE(SUM(quantity * price) / NULLIF(SUM(quantity), 0), 0) as vwap
                    FROM fills 
                    WHERE symbol = $1
                ",
                None,
                None,
            )
        };

        let vwap: Decimal = if let (Some(start), Some(end)) = (start, end) {
            sqlx::query_scalar(query)
                .bind(symbol)
                .bind(start)
                .bind(end)
                .fetch_one(&self.pool)
                .await?
        } else {
            sqlx::query_scalar(query)
                .bind(symbol)
                .fetch_one(&self.pool)
                .await?
        };

        Ok(vwap)
    }

    async fn get_hourly_execution_summary(
        &self,
        date: NaiveDate,
    ) -> Result<Vec<HourlyExecutionSummary>> {
        // and_hms_opt(0, 0, 0) is always valid for midnight
        let start_date = date
            .and_hms_opt(0, 0, 0)
            .unwrap_or_default()
            .and_utc();
        let end_date = date
            .succ_opt()
            .ok_or_else(|| {
                crate::RepositoryError::Validation("Date overflow computing next day".into())
            })?
            .and_hms_opt(0, 0, 0)
            .unwrap_or_default()
            .and_utc();

        let rows = sqlx::query(
            r"
            SELECT 
                    EXTRACT(HOUR FROM execution_timestamp) as hour,
                    COUNT(*) as execution_count,
                    COALESCE(SUM(quantity), 0) as total_volume,
                    COALESCE(SUM(quantity * price), 0) as total_value,
                    COALESCE(AVG(price), 0) as avg_price,
                    COUNT(DISTINCT symbol) as unique_symbols
                FROM fills 
                WHERE execution_timestamp >= $1 AND execution_timestamp < $2
                GROUP BY EXTRACT(HOUR FROM execution_timestamp)
            ORDER BY hour
            ",
        )
        .bind(start_date)
        .bind(end_date)
        .fetch_all(&self.pool)
        .await?;

        let mut summaries = Vec::new();
        for row in rows {
            summaries.push(HourlyExecutionSummary {
                hour: row.get::<i32, _>("hour"),
                execution_count: row.get("execution_count"),
                total_volume: row.get("total_volume"),
                total_value: row.get("total_value"),
                avg_price: row.get("avg_price"),
                unique_symbols: row.get("unique_symbols"),
            });
        }

        Ok(summaries)
    }

    async fn find_high_slippage_executions(
        &self,
        slippage_threshold: Decimal,
    ) -> Result<Vec<ExecutionWithSlippage>> {
        let executions = self.find_by_filter(&ExecutionFilter::new()).await?;

        // To compute slippage we need a reference price (the decision-time price).
        // We use VWAP for each symbol as the reference price, computed from all
        // executions of that symbol in the result set. This gives a fair benchmark:
        // executions priced far from the volume-weighted average had real slippage.
        let mut symbol_vwap: std::collections::HashMap<String, (Decimal, Decimal)> =
            std::collections::HashMap::new();
        for exec in &executions {
            let entry = symbol_vwap
                .entry(exec.symbol.clone())
                .or_insert((Decimal::ZERO, Decimal::ZERO));
            // Accumulate (price * quantity, quantity) for VWAP = sum(pq) / sum(q)
            entry.0 += exec.price * exec.quantity;
            entry.1 += exec.quantity;
        }

        let mut high_slippage_executions = Vec::new();
        for execution in executions {
            // Reference price: VWAP for this symbol.
            // If only one execution exists, VWAP = execution price, so slippage = 0,
            // which is correct (no basis for comparison).
            let expected_price = symbol_vwap
                .get(&execution.symbol)
                .and_then(|(total_value, total_qty)| {
                    if total_qty.is_zero() {
                        None
                    } else {
                        Some(*total_value / *total_qty)
                    }
                })
                .unwrap_or(execution.price);

            let slippage = execution.price - expected_price;
            let slippage_bps = if expected_price.is_zero() {
                Decimal::ZERO
            } else {
                slippage / expected_price * Decimal::from(10000)
            };

            if slippage_bps.abs() >= slippage_threshold {
                high_slippage_executions.push(ExecutionWithSlippage {
                    execution,
                    expected_price,
                    slippage,
                    slippage_bps,
                });
            }
        }

        Ok(high_slippage_executions)
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;
    use common::{Execution, OrderSide};
    use rust_decimal_macros::dec;
    use uuid::Uuid;

    #[test]
    fn test_execution_filter_builder() {
        let order_id = Uuid::new_v4();
        let filter = ExecutionFilter::new()
            .symbol("EURUSD")
            .order_id(order_id)
            .side(OrderSide::Buy)
            .limit(100);

        assert_eq!(filter.symbol.as_ref().unwrap(), "EURUSD");
        assert_eq!(filter.order_id.unwrap(), order_id);
        assert_eq!(filter.side.unwrap(), OrderSide::Buy);
        assert_eq!(filter.limit.unwrap(), 100);
    }

    #[test]
    fn test_execution_stats() {
        let stats = ExecutionStats {
            total_executions: 1000,
            buy_executions: 600,
            sell_executions: 400,
            total_volume: dec!(50000000),
            total_value: dec!(55000000),
            total_fees: dec!(5500),
            avg_execution_size: dec!(50000),
            avg_price: dec!(1.1000),
            largest_execution: Some(dec!(500000)),
            smallest_execution: Some(dec!(1000)),
            unique_symbols: 15,
            unique_venues: 3,
        };

        assert_eq!(stats.total_executions, 1000);
        assert_eq!(stats.buy_executions, 600);
        assert_eq!(stats.total_fees, dec!(5500));
    }

    #[test]
    fn test_hourly_execution_summary() {
        let summary = HourlyExecutionSummary {
            hour: 14,
            execution_count: 150,
            total_volume: dec!(5000000),
            total_value: dec!(5500000),
            avg_price: dec!(1.1000),
            unique_symbols: 8,
        };

        assert_eq!(summary.hour, 14);
        assert_eq!(summary.execution_count, 150);
        assert_eq!(summary.unique_symbols, 8);
    }

    #[test]
    fn test_execution_with_slippage() {
        let execution = Execution::new(
            Uuid::new_v4(),
            "EURUSD".to_owned(),
            dec!(100000),
            dec!(1.1005),
            OrderSide::Buy,
            dec!(5.0),
        );

        let execution_with_slippage = ExecutionWithSlippage {
            execution: execution.clone(),
            expected_price: dec!(1.1000),
            slippage: dec!(0.0005),
            slippage_bps: dec!(4.545), // Approximately 4.5 bps
        };

        assert_eq!(execution_with_slippage.expected_price, dec!(1.1000));
        assert_eq!(execution_with_slippage.slippage, dec!(0.0005));
        assert!(execution_with_slippage.slippage_bps > dec!(4.0));
        assert!(execution_with_slippage.slippage_bps < dec!(5.0));
        assert_eq!(execution.gross_value, dec!(110050)); // 100000 * 1.1005
        assert_eq!(execution.fees, dec!(5.0));
    }

    // Note: Database integration tests would require a test database
    // and are typically run separately from unit tests
}
