use serde::{Deserialize, Serialize};
use std::collections::VecDeque;

/// Tracks bandwidth metrics for a validator
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BandwidthMetrics {
    /// Total bytes sent
    pub bytes_sent: u64,

    /// Total bytes received
    pub bytes_received: u64,

    /// Peak bandwidth (bytes/second)
    pub peak_bandwidth: u64,

    /// Average bandwidth (bytes/second)
    pub avg_bandwidth: u64,

    /// Measurement period (seconds)
    pub measurement_period: u64,
}

impl BandwidthMetrics {
    pub fn new() -> Self {
        Self {
            bytes_sent: 0,
            bytes_received: 0,
            peak_bandwidth: 0,
            avg_bandwidth: 0,
            measurement_period: 0,
        }
    }

    /// Total bandwidth (sent + received)
    pub fn total_bytes(&self) -> u64 {
        self.bytes_sent + self.bytes_received
    }

    /// Calculate bandwidth score (normalized 0.0 - 1.0)
    /// 1 Mbps = 125 KB/s = baseline score of 0.5
    pub fn score(&self) -> f64 {
        if self.measurement_period == 0 {
            return 0.0;
        }

        let bytes_per_sec = self.total_bytes() / self.measurement_period;

        // Baseline: 125 KB/s (1 Mbps) = 0.5 score
        // 10 Mbps = 1.0 score
        const MAX_BYTES_PER_SEC: u64 = 1_250_000; // 10 Mbps

        let score = bytes_per_sec as f64 / MAX_BYTES_PER_SEC as f64;

        score.clamp(0.0, 1.0)
    }

    /// Update metrics with new measurement
    pub fn update(&mut self, bytes_sent: u64, bytes_received: u64, period_secs: u64) {
        self.bytes_sent += bytes_sent;
        self.bytes_received += bytes_received;
        self.measurement_period += period_secs;

        if period_secs > 0 {
            let current_bandwidth = (bytes_sent + bytes_received) / period_secs;
            self.peak_bandwidth = self.peak_bandwidth.max(current_bandwidth);

            // Update rolling average
            let total = self.total_bytes();
            self.avg_bandwidth = total / self.measurement_period;
        }
    }
}

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

/// Tracks uptime metrics for a validator
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UptimeMetrics {
    /// Total uptime in seconds
    pub total_uptime: u64,

    /// Total time window (seconds)
    pub total_time: u64,

    /// Number of disconnections
    pub disconnections: u32,

    /// Longest continuous uptime (seconds)
    pub longest_uptime: u64,

    /// Current uptime streak (seconds)
    pub current_streak: u64,

    /// Last seen timestamp
    pub last_seen: i64,
}

impl UptimeMetrics {
    pub fn new(current_time: i64) -> Self {
        Self {
            total_uptime: 0,
            total_time: 0,
            disconnections: 0,
            longest_uptime: 0,
            current_streak: 0,
            last_seen: current_time,
        }
    }

    /// Calculate uptime percentage
    pub fn uptime_percentage(&self) -> f64 {
        if self.total_time == 0 {
            return 0.0;
        }

        (self.total_uptime as f64 / self.total_time as f64) * 100.0
    }

    /// Calculate uptime score (0.0 - 1.0)
    /// Factors: uptime %, disconnection rate, streak length
    pub fn score(&self) -> f64 {
        let uptime_score = self.uptime_percentage() / 100.0;

        // Penalty for frequent disconnections
        let disconnection_penalty = if self.total_time > 0 {
            let disconnects_per_day =
                (self.disconnections as f64) / (self.total_time as f64 / 86400.0);

            // More than 5 disconnects/day = penalty
            if disconnects_per_day > 5.0 {
                0.5
            } else if disconnects_per_day > 2.0 {
                0.8
            } else {
                1.0
            }
        } else {
            1.0
        };

        // Bonus for long streaks
        let streak_bonus = if self.current_streak > 7 * 86400 {
            // > 7 days
            1.2
        } else if self.current_streak > 24 * 3600 {
            // > 24 hours
            1.1
        } else {
            1.0
        };

        (uptime_score * disconnection_penalty * streak_bonus).clamp(0.0, 1.0)
    }

    /// Record heartbeat (node is online)
    pub fn record_heartbeat(&mut self, current_time: i64) {
        let elapsed = (current_time - self.last_seen).max(0) as u64;

        // If gap is small (< 5 min), consider continuous
        if elapsed < 300 {
            self.total_uptime += elapsed;
            self.current_streak += elapsed;

            if self.current_streak > self.longest_uptime {
                self.longest_uptime = self.current_streak;
            }
        } else {
            // Disconnection detected
            self.disconnections += 1;
            self.current_streak = 0;
        }

        self.total_time += elapsed;
        self.last_seen = current_time;
    }

    /// Record explicit disconnection
    pub fn record_disconnection(&mut self, current_time: i64) {
        self.disconnections += 1;
        self.current_streak = 0;
        self.last_seen = current_time;
    }
}

/// Tracks storage contribution metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageMetrics {
    /// Storage space provided (bytes)
    pub storage_provided: u64,

    /// Storage actually used (bytes)
    pub storage_used: u64,

    /// Number of blocks stored
    pub blocks_stored: u64,

    /// Number of transactions stored
    pub transactions_stored: u64,
}

impl StorageMetrics {
    pub fn new() -> Self {
        Self {
            storage_provided: 0,
            storage_used: 0,
            blocks_stored: 0,
            transactions_stored: 0,
        }
    }

    /// Calculate utilization percentage
    pub fn utilization(&self) -> f64 {
        if self.storage_provided == 0 {
            return 0.0;
        }

        (self.storage_used as f64 / self.storage_provided as f64) * 100.0
    }

    /// Calculate storage score (0.0 - 1.0)
    /// 10 GB provided = baseline 0.5
    /// 100 GB provided = 1.0
    pub fn score(&self) -> f64 {
        const MAX_GB: u64 = 100;

        let gb_provided = self.storage_provided / (1024 * 1024 * 1024);

        let score = gb_provided as f64 / MAX_GB as f64;

        score.clamp(0.0, 1.0)
    }

    /// Update storage metrics
    pub fn update(&mut self, provided: u64, used: u64, blocks: u64, txs: u64) {
        self.storage_provided = provided;
        self.storage_used = used;
        self.blocks_stored = blocks;
        self.transactions_stored = txs;
    }
}

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

/// Combined metrics tracker for a validator
#[derive(Debug)]
pub struct MetricsTracker {
    pub bandwidth: BandwidthMetrics,
    pub uptime: UptimeMetrics,
    pub storage: StorageMetrics,

    /// History of bandwidth measurements
    bandwidth_history: VecDeque<BandwidthMetrics>,

    /// Maximum history entries to keep
    max_history: usize,
}

impl MetricsTracker {
    pub fn new(current_time: i64) -> Self {
        Self {
            bandwidth: BandwidthMetrics::new(),
            uptime: UptimeMetrics::new(current_time),
            storage: StorageMetrics::new(),
            bandwidth_history: VecDeque::new(),
            max_history: 24, // 24 hours of hourly measurements
        }
    }

    /// Record heartbeat
    pub fn heartbeat(&mut self, current_time: i64) {
        self.uptime.record_heartbeat(current_time);
    }

    /// Update bandwidth
    pub fn update_bandwidth(&mut self, bytes_sent: u64, bytes_received: u64, period_secs: u64) {
        self.bandwidth.update(bytes_sent, bytes_received, period_secs);

        // Save to history
        self.bandwidth_history.push_back(self.bandwidth.clone());

        if self.bandwidth_history.len() > self.max_history {
            self.bandwidth_history.pop_front();
        }
    }

    /// Update storage
    pub fn update_storage(&mut self, provided: u64, used: u64, blocks: u64, txs: u64) {
        self.storage.update(provided, used, blocks, txs);
    }

    /// Get average bandwidth score over history
    pub fn avg_bandwidth_score(&self) -> f64 {
        if self.bandwidth_history.is_empty() {
            return self.bandwidth.score();
        }

        let total: f64 = self.bandwidth_history.iter().map(|b| b.score()).sum();

        total / self.bandwidth_history.len() as f64
    }

    /// Get combined metrics score
    pub fn combined_score(&self) -> f64 {
        let bandwidth_score = self.avg_bandwidth_score();
        let uptime_score = self.uptime.score();
        let storage_score = self.storage.score();

        // Weighted average
        bandwidth_score * 0.4 + uptime_score * 0.4 + storage_score * 0.2
    }
}

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

    #[test]
    fn test_bandwidth_metrics() {
        let mut metrics = BandwidthMetrics::new();

        // 1 MB sent + received over 10 seconds = 100 KB/s
        metrics.update(500_000, 500_000, 10);

        assert_eq!(metrics.total_bytes(), 1_000_000);
        assert_eq!(metrics.avg_bandwidth, 100_000);
    }

    #[test]
    fn test_uptime_metrics() {
        let mut metrics = UptimeMetrics::new(0);

        // Record heartbeats every minute for 1 hour
        for i in 1..=60 {
            metrics.record_heartbeat(i * 60);
        }

        assert!(metrics.uptime_percentage() > 99.0);
        assert_eq!(metrics.disconnections, 0);
    }

    #[test]
    fn test_uptime_with_disconnections() {
        let mut metrics = UptimeMetrics::new(0);

        metrics.record_heartbeat(60); // 1 min
        metrics.record_heartbeat(120); // 2 min
        metrics.record_heartbeat(600); // 10 min (gap = disconnection)

        assert_eq!(metrics.disconnections, 1);
    }

    #[test]
    fn test_storage_metrics() {
        let mut metrics = StorageMetrics::new();

        // 50 GB provided, 25 GB used
        metrics.update(
            50 * 1024 * 1024 * 1024,
            25 * 1024 * 1024 * 1024,
            1000,
            50000,
        );

        assert_eq!(metrics.utilization(), 50.0);
        assert!(metrics.score() > 0.0);
    }

    #[test]
    fn test_metrics_tracker() {
        let mut tracker = MetricsTracker::new(0);

        tracker.heartbeat(60);
        tracker.update_bandwidth(1_000_000, 1_000_000, 60);
        tracker.update_storage(10 * 1024 * 1024 * 1024, 5 * 1024 * 1024 * 1024, 100, 1000);

        let score = tracker.combined_score();
        assert!(score > 0.0);
        assert!(score <= 1.0);
    }
}
