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

/// Proof that a validator relayed traffic through Tor for OnionCoin network
/// This is OnionCoin's UNIQUE feature: earn rewards by being a Tor relay!
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelayProof {
    /// Validator's public key
    pub validator_pubkey: [u8; 32],

    /// Time period this proof covers (Unix timestamp range)
    pub period_start: i64,
    pub period_end: i64,

    /// Metrics for this period
    pub metrics: RelayMetrics,

    /// Cryptographic proof of relay work
    /// In production: ZK-SNARK proof of bandwidth relay
    /// For prototype: hash chain of relayed packet hashes
    pub proof_data: Vec<u8>,

    /// Signature from validator
    #[serde(with = "serde_bytes")]
    pub signature: [u8; 64],
}

impl RelayProof {
    /// Standard proof period (1 hour)
    pub const PROOF_PERIOD_SECONDS: i64 = 3600;

    /// Create a new relay proof
    pub fn new(
        validator_pubkey: [u8; 32],
        period_start: i64,
        period_end: i64,
        metrics: RelayMetrics,
    ) -> Self {
        // Generate proof data (simplified)
        let proof_data = Self::generate_proof_data(&metrics);

        Self {
            validator_pubkey,
            period_start,
            period_end,
            metrics,
            proof_data,
            signature: [0u8; 64], // Placeholder
        }
    }

    /// Verify relay proof
    pub fn verify(&self) -> bool {
        // Check time period is valid
        if self.period_end <= self.period_start {
            return false;
        }

        // Check period is not too long
        if self.period_end - self.period_start > Self::PROOF_PERIOD_SECONDS * 24 {
            return false;
        }

        // Verify metrics are reasonable
        if !self.metrics.is_valid() {
            return false;
        }

        // Verify proof data
        let expected_proof = Self::generate_proof_data(&self.metrics);
        if self.proof_data != expected_proof {
            return false;
        }

        true
    }

    /// Generate proof data from metrics
    fn generate_proof_data(metrics: &RelayMetrics) -> Vec<u8> {
        let mut hasher = Hasher::new();
        hasher.update(&metrics.bytes_relayed.to_le_bytes());
        hasher.update(&metrics.packets_relayed.to_le_bytes());
        hasher.update(&metrics.unique_circuits.to_le_bytes());
        hasher.finalize().as_bytes().to_vec()
    }

    /// Calculate reward for this relay proof
    pub fn calculate_reward(&self) -> u64 {
        // Base reward per GB relayed
        const REWARD_PER_GB: u64 = 1; // 1 ONC per GB

        let gb_relayed = self.metrics.bytes_relayed / (1024 * 1024 * 1024);

        // Bonus for high circuit diversity
        let circuit_bonus = if self.metrics.unique_circuits > 100 {
            1.2
        } else if self.metrics.unique_circuits > 50 {
            1.1
        } else {
            1.0
        };

        ((gb_relayed * REWARD_PER_GB) as f64 * circuit_bonus) as u64
    }
}

/// Metrics about relay activity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelayMetrics {
    /// Total bytes relayed in this period
    pub bytes_relayed: u64,

    /// Total packets relayed
    pub packets_relayed: u64,

    /// Number of unique circuits served
    pub unique_circuits: u32,

    /// Average latency (milliseconds)
    pub avg_latency_ms: u32,

    /// Number of failed relay attempts
    pub failed_relays: u32,

    /// Uptime during this period (seconds)
    pub uptime_seconds: u64,
}

impl RelayMetrics {
    pub fn new() -> Self {
        Self {
            bytes_relayed: 0,
            packets_relayed: 0,
            unique_circuits: 0,
            avg_latency_ms: 0,
            failed_relays: 0,
            uptime_seconds: 0,
        }
    }

    /// Check if metrics are valid (sanity checks)
    pub fn is_valid(&self) -> bool {
        // Reasonable bounds
        if self.bytes_relayed > 1_000_000_000_000 {
            // > 1 TB
            return false;
        }

        if self.packets_relayed > 1_000_000_000 {
            // > 1B packets
            return false;
        }

        if self.avg_latency_ms > 60_000 {
            // > 1 minute
            return false;
        }

        // Packets should roughly match bytes
        if self.packets_relayed > 0 {
            let avg_packet_size = self.bytes_relayed / self.packets_relayed;
            if avg_packet_size < 20 || avg_packet_size > 1_000_000 {
                // Unrealistic packet size
                return false;
            }
        }

        true
    }

    /// Calculate quality score (0.0 - 1.0)
    pub fn quality_score(&self) -> f64 {
        let mut score = 1.0;

        // Penalty for high latency
        if self.avg_latency_ms > 5000 {
            score *= 0.5;
        } else if self.avg_latency_ms > 2000 {
            score *= 0.8;
        }

        // Penalty for failed relays
        if self.packets_relayed > 0 {
            let failure_rate = self.failed_relays as f64 / self.packets_relayed as f64;
            score *= (1.0 - failure_rate).max(0.0);
        }

        score.clamp(0.0, 1.0)
    }
}

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

/// Tracks relay activity for a validator over time
#[derive(Debug)]
pub struct RelayTracker {
    /// Validator public key
    validator_pubkey: [u8; 32],

    /// Current period metrics
    current_metrics: RelayMetrics,

    /// Period start time
    period_start: i64,

    /// Historical proofs (last 24 hours)
    history: VecDeque<RelayProof>,
}

impl RelayTracker {
    /// Maximum history to keep (24 hours = 24 proofs)
    const MAX_HISTORY: usize = 24;

    pub fn new(validator_pubkey: [u8; 32], current_time: i64) -> Self {
        Self {
            validator_pubkey,
            current_metrics: RelayMetrics::new(),
            period_start: current_time,
            history: VecDeque::new(),
        }
    }

    /// Record relayed bytes
    pub fn record_relay(&mut self, bytes: u64, latency_ms: u32, success: bool) {
        self.current_metrics.bytes_relayed += bytes;
        self.current_metrics.packets_relayed += 1;

        // Update average latency (simple moving average)
        let total = self.current_metrics.packets_relayed;
        self.current_metrics.avg_latency_ms =
            ((self.current_metrics.avg_latency_ms as u64 * (total - 1) + latency_ms as u64)
                / total) as u32;

        if !success {
            self.current_metrics.failed_relays += 1;
        }
    }

    /// Record a unique circuit
    pub fn record_circuit(&mut self) {
        self.current_metrics.unique_circuits += 1;
    }

    /// Record uptime
    pub fn record_uptime(&mut self, seconds: u64) {
        self.current_metrics.uptime_seconds += seconds;
    }

    /// Finalize current period and generate proof
    pub fn finalize_period(&mut self, current_time: i64) -> RelayProof {
        let proof = RelayProof::new(
            self.validator_pubkey,
            self.period_start,
            current_time,
            self.current_metrics.clone(),
        );

        // Add to history
        self.history.push_back(proof.clone());

        // Trim history
        if self.history.len() > Self::MAX_HISTORY {
            self.history.pop_front();
        }

        // Reset for next period
        self.current_metrics = RelayMetrics::new();
        self.period_start = current_time;

        proof
    }

    /// Get total bytes relayed in last 24 hours
    pub fn get_24h_bytes(&self) -> u64 {
        self.history
            .iter()
            .map(|p| p.metrics.bytes_relayed)
            .sum::<u64>()
            + self.current_metrics.bytes_relayed
    }

    /// Get average quality score over last 24 hours
    pub fn get_avg_quality(&self) -> f64 {
        if self.history.is_empty() {
            return self.current_metrics.quality_score();
        }

        let total: f64 = self
            .history
            .iter()
            .map(|p| p.metrics.quality_score())
            .sum();

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

    /// Get current metrics
    pub fn current_metrics(&self) -> &RelayMetrics {
        &self.current_metrics
    }

    /// Get proof history
    pub fn history(&self) -> &VecDeque<RelayProof> {
        &self.history
    }
}

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

    #[test]
    fn test_relay_metrics_validation() {
        let mut metrics = RelayMetrics::new();
        metrics.bytes_relayed = 1_000_000; // 1 MB
        metrics.packets_relayed = 1000;
        metrics.avg_latency_ms = 100;

        assert!(metrics.is_valid());

        // Invalid: too many bytes
        metrics.bytes_relayed = 2_000_000_000_000;
        assert!(!metrics.is_valid());
    }

    #[test]
    fn test_relay_proof_creation() {
        let pubkey = [1u8; 32];
        let mut metrics = RelayMetrics::new();
        metrics.bytes_relayed = 1_000_000_000; // 1 GB
        metrics.packets_relayed = 10_000;
        metrics.unique_circuits = 50;

        let proof = RelayProof::new(pubkey, 0, 3600, metrics);

        assert!(proof.verify());
        assert_eq!(proof.calculate_reward(), 1); // 1 ONC for 1 GB
    }

    #[test]
    fn test_relay_tracker() {
        let pubkey = [1u8; 32];
        let mut tracker = RelayTracker::new(pubkey, 0);

        // Record some relay activity
        tracker.record_relay(1000, 100, true);
        tracker.record_relay(2000, 150, true);
        tracker.record_circuit();

        assert_eq!(tracker.current_metrics().bytes_relayed, 3000);
        assert_eq!(tracker.current_metrics().packets_relayed, 2);
        assert_eq!(tracker.current_metrics().unique_circuits, 1);

        // Finalize period
        let proof = tracker.finalize_period(3600);
        assert!(proof.verify());
        assert_eq!(tracker.history().len(), 1);
    }

    #[test]
    fn test_quality_score() {
        let mut metrics = RelayMetrics::new();
        metrics.packets_relayed = 100;
        metrics.failed_relays = 10; // 10% failure rate
        metrics.avg_latency_ms = 1000;

        let score = metrics.quality_score();
        assert!(score < 1.0);
        assert!(score > 0.0);
    }

    #[test]
    fn test_reward_calculation() {
        let pubkey = [1u8; 32];

        // 5 GB relayed with good circuit diversity
        let mut metrics = RelayMetrics::new();
        metrics.bytes_relayed = 5 * 1024 * 1024 * 1024;
        metrics.unique_circuits = 150; // High diversity = bonus

        let proof = RelayProof::new(pubkey, 0, 3600, metrics);
        let reward = proof.calculate_reward();

        assert!(reward >= 5); // At least 5 ONC base
        assert!(reward > 5); // Should have bonus
    }
}
