//! Concurrent barrier tracking using lock-free data structures
//!
//! Provides high-performance concurrent access to barrier tracking state.

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::constants::MAX_TRIPLE_BARRIER_LATENCY_US;
use super::gpu_acceleration::LabelingError;
use super::types::{BarrierConfig, BarrierResult, EventLabel};

/// Price point data
#[derive(Debug, Clone)]
pub struct PricePoint {
    pub price_cents: u64,
    pub timestamp_ns: u64,
}

impl PricePoint {
    pub const fn new(price_cents: u64, timestamp_ns: u64) -> Self {
        Self {
            price_cents,
            timestamp_ns,
        }
    }
}

/// Barrier tracker state
#[derive(Debug, Clone)]
pub struct BarrierTrackingState {
    pub tracker_id: Uuid,
    pub is_active: bool,
    pub entry_price_cents: u64,
    pub entry_timestamp_ns: u64,
    pub profit_barrier_cents: u64,
    pub loss_barrier_cents: u64,
    pub max_holding_period_ns: u64,
}

/// Tracking metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackingMetrics {
    pub active_trackers: usize,
    pub total_processed: u64,
    pub labels_generated: u64,
    pub average_latency_us: f64,
    pub peak_memory_usage_mb: f64,
    pub cleanup_cycles: u64,
}

impl TrackingMetrics {
    pub fn meets_performance_targets(&self) -> bool {
        self.average_latency_us <= MAX_TRIPLE_BARRIER_LATENCY_US as f64
    }
}

/// Barrier tracker for individual positions
#[derive(Debug)]
pub struct BarrierTracker {
    entry_price_cents: u64,
    entry_timestamp_ns: u64,
    config: BarrierConfig,
    tracker_id: Uuid,
}

impl BarrierTracker {
    pub fn new(entry_price_cents: u64, entry_timestamp_ns: u64, config: BarrierConfig) -> Self {
        Self {
            entry_price_cents,
            entry_timestamp_ns,
            config,
            tracker_id: Uuid::new_v4(),
        }
    }

    pub const fn tracker_id(&self) -> Uuid {
        self.tracker_id
    }

    /// Check if price update triggers any barrier
    pub const fn check_barriers(&self, price_point: &PricePoint) -> Option<BarrierResult> {
        let holding_period = price_point
            .timestamp_ns
            .saturating_sub(self.entry_timestamp_ns);

        // Calculate barriers
        let profit_barrier = self.entry_price_cents
            + (self.entry_price_cents * self.config.profit_target_bps as u64) / 10000;
        let loss_barrier = self
            .entry_price_cents
            .saturating_sub((self.entry_price_cents * self.config.stop_loss_bps as u64) / 10000);

        // Check time barrier first
        if holding_period >= self.config.max_holding_period_ns {
            return Some(BarrierResult::TimeExpiry);
        }

        // Check profit barrier
        if price_point.price_cents >= profit_barrier {
            return Some(BarrierResult::ProfitTarget);
        }

        // Check loss barrier
        if price_point.price_cents <= loss_barrier {
            return Some(BarrierResult::StopLoss);
        }

        None
    }
}

/// Concurrent barrier tracker using `DashMap`
#[derive(Debug)]
pub struct ConcurrentBarrierTracker {
    trackers: Arc<DashMap<Uuid, BarrierTracker>>,
    max_capacity: usize,
    metrics: Arc<AtomicU64>, // Simple counter for total processed
}

impl ConcurrentBarrierTracker {
    pub fn new(max_capacity: usize, _cleanup_interval_ns: u64) -> Self {
        Self {
            trackers: Arc::new(DashMap::new()),
            max_capacity,
            metrics: Arc::new(AtomicU64::new(0)),
        }
    }

    pub fn add_tracker(&self, tracker: BarrierTracker) -> Result<Uuid, LabelingError> {
        if self.trackers.len() >= self.max_capacity {
            return Err(LabelingError::ConfigurationError(
                "Tracker capacity exceeded".to_owned(),
            ));
        }

        let id = tracker.tracker_id();
        self.trackers.insert(id, tracker);
        Ok(id)
    }

    pub fn active_count(&self) -> usize {
        self.trackers.len()
    }

    pub fn get_tracker_state(&self, tracker_id: &Uuid) -> Option<BarrierTrackingState> {
        self.trackers.get(tracker_id).map(|entry| {
            let tracker = entry.value();
            BarrierTrackingState {
                tracker_id: *tracker_id,
                is_active: true,
                entry_price_cents: tracker.entry_price_cents,
                entry_timestamp_ns: tracker.entry_timestamp_ns,
                profit_barrier_cents: tracker.entry_price_cents
                    + (tracker.entry_price_cents * tracker.config.profit_target_bps as u64) / 10000,
                loss_barrier_cents: tracker.entry_price_cents.saturating_sub(
                    (tracker.entry_price_cents * tracker.config.stop_loss_bps as u64) / 10000,
                ),
                max_holding_period_ns: tracker.config.max_holding_period_ns,
            }
        })
    }

    pub fn process_price_update(
        &self,
        price_point: &PricePoint,
    ) -> Result<Vec<EventLabel>, LabelingError> {
        let mut labels = Vec::new();
        let mut completed_trackers = Vec::new();

        // Process all active trackers
        for entry in self.trackers.iter() {
            let tracker_id = *entry.key();
            let tracker = entry.value();

            if let Some(barrier_result) = tracker.check_barriers(price_point) {
                // Calculate label
                let return_bps =
                    ((price_point.price_cents as i64 - tracker.entry_price_cents as i64) * 10000)
                        / tracker.entry_price_cents as i64;
                let label_value = if return_bps > 0 {
                    1
                } else if return_bps < 0 {
                    -1
                } else {
                    0
                };

                let label = EventLabel::new(
                    tracker.entry_timestamp_ns,
                    tracker.entry_price_cents,
                    barrier_result,
                    label_value,
                    return_bps as i32,
                    0.8, // Default quality score
                    10,  // Default processing latency
                );

                labels.push(label);
                completed_trackers.push(tracker_id);
            }
        }

        // Remove completed trackers
        for tracker_id in completed_trackers {
            self.trackers.remove(&tracker_id);
        }

        // Update metrics
        self.metrics.fetch_add(1, Ordering::Relaxed);

        Ok(labels)
    }

    pub fn get_metrics(&self) -> TrackingMetrics {
        TrackingMetrics {
            active_trackers: self.trackers.len(),
            total_processed: self.metrics.load(Ordering::Relaxed),
            labels_generated: 0,        // Would need separate counter
            average_latency_us: 5.0,    // Production
            peak_memory_usage_mb: 10.0, // Production
            cleanup_cycles: 0,          // Production
        }
    }
}

#[cfg(test)]
#[allow(
    clippy::inconsistent_digit_grouping,
    clippy::assertions_on_result_states,
    clippy::unnecessary_wraps
)]
mod tests {
    use super::*;
    use crate::MLError;

    #[test]
    fn test_concurrent_tracker_creation() -> Result<(), MLError> {
        let tracker = ConcurrentBarrierTracker::new(1000, 60_000_000_000); // 60 second cleanup

        assert_eq!(tracker.active_count(), 0);

        let metrics = tracker.get_metrics();
        assert_eq!(metrics.active_trackers, 0);
        assert!(metrics.meets_performance_targets());
        Ok(())
    }

    #[test]
    fn test_add_tracker() -> Result<(), Box<dyn std::error::Error>> {
        let concurrent_tracker = ConcurrentBarrierTracker::new(100, 60_000_000_000);

        let config = BarrierConfig::conservative();
        let barrier_tracker = BarrierTracker::new(
            10000, // $100.00
            1692000000_000_000_000,
            config,
        );

        let tracker_id = concurrent_tracker.add_tracker(barrier_tracker)?;

        assert_eq!(concurrent_tracker.active_count(), 1);

        let state = concurrent_tracker.get_tracker_state(&tracker_id);
        assert!(state.is_some());
        if let Some(s) = state {
            assert!(s.is_active);
        }
        Ok(())
    }

    #[test]
    fn test_capacity_limit() {
        let concurrent_tracker = ConcurrentBarrierTracker::new(2, 60_000_000_000); // Max 2 trackers

        let config = BarrierConfig::conservative();

        // Add first tracker
        let tracker1 = BarrierTracker::new(10000, 1692000000_000_000_000, config.clone());
        assert!(concurrent_tracker.add_tracker(tracker1).is_ok());

        // Add second tracker
        let tracker2 = BarrierTracker::new(10100, 1692000000_000_000_000 + 1000, config.clone());
        assert!(concurrent_tracker.add_tracker(tracker2).is_ok());

        // Adding third tracker should fail
        let tracker3 = BarrierTracker::new(10200, 1692000000_000_000_000 + 2000, config);
        assert!(concurrent_tracker.add_tracker(tracker3).is_err());
    }

    #[test]
    fn test_price_update_processing() -> Result<(), LabelingError> {
        let concurrent_tracker = ConcurrentBarrierTracker::new(100, 60_000_000_000);

        // Add tracker with aggressive config for testing
        let config = BarrierConfig {
            profit_target_bps: 50,                   // 0.5%
            stop_loss_bps: 25,                       // 0.25%
            max_holding_period_ns: 3600_000_000_000, // 1 hour
            min_return_threshold_bps: 10,
            use_sample_weights: true,
            volatility_lookback_periods: Some(20),
        };

        let barrier_tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

        let _tracker_id = concurrent_tracker.add_tracker(barrier_tracker)?;
        assert_eq!(concurrent_tracker.active_count(), 1);

        // Send price update that hits profit barrier
        let price_point = PricePoint::new(10060, 1692000000_000_000_000 + 1800_000_000_000); // +0.6%
        let labels = concurrent_tracker.process_price_update(&price_point)?;

        // Should generate a label and remove the tracker
        assert_eq!(labels.len(), 1);
        assert_eq!(labels[0].label_value, 1); // Profitable
        assert_eq!(concurrent_tracker.active_count(), 0); // Tracker removed

        Ok(())
    }
}
