//! Compute L2 Usage Metering

use serde::{Deserialize, Serialize};

/// Compute service usage metrics
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ComputeUsage {
    /// CPU core-seconds consumed
    pub cpu_core_seconds: u64,
    /// GPU seconds consumed
    pub gpu_seconds: u64,
    /// Memory GB-seconds consumed
    pub memory_gb_seconds: u64,
    /// Number of serverless invocations
    pub invocations: u64,
    /// TPU seconds consumed
    pub tpu_seconds: u64,
}

impl ComputeUsage {
    /// Create new compute usage
    pub fn new() -> Self {
        Self::default()
    }

    /// Add CPU usage
    pub fn with_cpu(mut self, core_seconds: u64) -> Self {
        self.cpu_core_seconds = core_seconds;
        self
    }

    /// Add GPU usage
    pub fn with_gpu(mut self, seconds: u64) -> Self {
        self.gpu_seconds = seconds;
        self
    }

    /// Add memory usage
    pub fn with_memory(mut self, gb_seconds: u64) -> Self {
        self.memory_gb_seconds = gb_seconds;
        self
    }

    /// Add serverless invocations
    pub fn with_invocations(mut self, count: u64) -> Self {
        self.invocations = count;
        self
    }

    /// Add TPU usage
    pub fn with_tpu(mut self, seconds: u64) -> Self {
        self.tpu_seconds = seconds;
        self
    }

    /// Get CPU core-hours
    pub fn cpu_core_hours(&self) -> f64 {
        self.cpu_core_seconds as f64 / 3600.0
    }

    /// Get GPU hours
    pub fn gpu_hours(&self) -> f64 {
        self.gpu_seconds as f64 / 3600.0
    }

    /// Get memory GB-hours
    pub fn memory_gb_hours(&self) -> f64 {
        self.memory_gb_seconds as f64 / 3600.0
    }

    /// Get TPU hours
    pub fn tpu_hours(&self) -> f64 {
        self.tpu_seconds as f64 / 3600.0
    }

    /// Get invocations in millions
    pub fn invocations_millions(&self) -> f64 {
        self.invocations as f64 / 1_000_000.0
    }
}

/// GPU type for pricing differentiation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GpuType {
    /// NVIDIA RTX 3090
    Rtx3090,
    /// NVIDIA RTX 4090
    Rtx4090,
    /// NVIDIA A100
    A100,
    /// NVIDIA H100
    H100,
    /// AMD MI250
    Mi250,
    /// Generic GPU
    Generic,
}

impl GpuType {
    /// Get price multiplier relative to base GPU price
    pub fn price_multiplier(&self) -> f64 {
        match self {
            GpuType::Rtx3090 => 0.8,
            GpuType::Rtx4090 => 1.0,
            GpuType::A100 => 2.5,
            GpuType::H100 => 4.0,
            GpuType::Mi250 => 2.0,
            GpuType::Generic => 0.5,
        }
    }
}

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

    #[test]
    fn test_compute_usage() {
        let usage = ComputeUsage::new()
            .with_cpu(7200) // 2 core-hours
            .with_gpu(3600) // 1 GPU hour
            .with_memory(14400) // 4 GB-hours
            .with_invocations(1_000_000);

        assert_eq!(usage.cpu_core_hours(), 2.0);
        assert_eq!(usage.gpu_hours(), 1.0);
        assert_eq!(usage.memory_gb_hours(), 4.0);
        assert_eq!(usage.invocations_millions(), 1.0);
    }

    #[test]
    fn test_gpu_pricing() {
        assert!(GpuType::H100.price_multiplier() > GpuType::Rtx4090.price_multiplier());
        assert!(GpuType::Rtx4090.price_multiplier() > GpuType::Generic.price_multiplier());
    }
}
