//! Metering Service
//!
//! Real-time usage tracking for all Synor L2 services:
//! - Storage L2: bytes stored, retrieved, deals created
//! - Hosting: bandwidth, domains
//! - Database L2: queries, vector searches, storage
//! - Compute L2: CPU hours, GPU hours, memory, invocations
//! - Network: bandwidth consumed

mod compute;
mod database;
mod hosting;
mod network;
mod storage;

pub use compute::{ComputeUsage, GpuType};
pub use database::DatabaseUsage;
pub use hosting::HostingUsage;
pub use network::NetworkUsage;
pub use storage::StorageUsage;

use crate::error::{EconomicsError, Result};
use crate::pricing::PricingEngine;
use crate::{AccountId, ResourceUnit, ServiceId, ServiceType, SynorDecimal};
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

/// Usage event from L2 services
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageEvent {
    /// Unique event ID
    pub id: String,
    /// Account being charged
    pub account_id: AccountId,
    /// Service generating the usage
    pub service_id: ServiceId,
    /// Type of service
    pub service_type: ServiceType,
    /// Resource unit consumed
    pub resource_unit: ResourceUnit,
    /// Amount consumed
    pub amount: Decimal,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Additional metadata
    pub metadata: HashMap<String, String>,
}

impl UsageEvent {
    /// Create a new usage event
    pub fn new(
        account_id: impl Into<String>,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
        amount: Decimal,
    ) -> Self {
        Self {
            id: generate_event_id(),
            account_id: account_id.into(),
            service_id: String::new(),
            service_type,
            resource_unit,
            amount,
            timestamp: Utc::now(),
            metadata: HashMap::new(),
        }
    }

    /// Add service ID
    pub fn with_service_id(mut self, service_id: impl Into<String>) -> Self {
        self.service_id = service_id.into();
        self
    }

    /// Add metadata
    pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.metadata.insert(key.into(), value.into());
        self
    }
}

/// Generate unique event ID
fn generate_event_id() -> String {
    use std::time::{SystemTime, UNIX_EPOCH};
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    format!("evt_{:x}", nanos)
}

/// Aggregated usage record for a billing period
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageRecord {
    pub account_id: AccountId,
    pub period_start: DateTime<Utc>,
    pub period_end: DateTime<Utc>,
    pub by_service: HashMap<ServiceType, SynorDecimal>,
    pub by_resource: HashMap<ResourceUnit, Decimal>,
    pub total_cost: SynorDecimal,
    pub event_count: usize,
}

impl UsageRecord {
    /// Create empty usage record for a period
    pub fn new(account_id: impl Into<String>, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
        Self {
            account_id: account_id.into(),
            period_start: start,
            period_end: end,
            by_service: HashMap::new(),
            by_resource: HashMap::new(),
            total_cost: Decimal::ZERO,
            event_count: 0,
        }
    }
}

/// Account usage summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountUsage {
    pub account_id: AccountId,
    pub by_service: HashMap<ServiceType, SynorDecimal>,
    pub current_period_start: DateTime<Utc>,
    pub last_event: Option<DateTime<Utc>>,
}

/// Metering service configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MeteringConfig {
    /// Aggregation interval (default: 1 hour)
    pub aggregation_interval_secs: u64,
    /// Maximum events to buffer before flush
    pub buffer_size: usize,
    /// Enable real-time usage tracking
    pub real_time_tracking: bool,
    /// Event retention period in days
    pub retention_days: u32,
}

impl Default for MeteringConfig {
    fn default() -> Self {
        Self {
            aggregation_interval_secs: 3600, // 1 hour
            buffer_size: 10000,
            real_time_tracking: true,
            retention_days: 90,
        }
    }
}

/// Metering service for tracking resource usage
pub struct MeteringService {
    config: MeteringConfig,
    pricing: Arc<PricingEngine>,
    /// Buffered events pending aggregation
    event_buffer: RwLock<Vec<UsageEvent>>,
    /// Current period usage by account
    current_usage: RwLock<HashMap<AccountId, AccountUsage>>,
    /// Historical aggregated records
    records: RwLock<Vec<UsageRecord>>,
    /// Event channel for real-time processing
    event_tx: async_channel::Sender<UsageEvent>,
    event_rx: async_channel::Receiver<UsageEvent>,
}

impl MeteringService {
    /// Create a new metering service
    pub fn new(pricing: Arc<PricingEngine>) -> Self {
        let (event_tx, event_rx) = async_channel::bounded(10000);

        Self {
            config: MeteringConfig::default(),
            pricing,
            event_buffer: RwLock::new(Vec::new()),
            current_usage: RwLock::new(HashMap::new()),
            records: RwLock::new(Vec::new()),
            event_tx,
            event_rx,
        }
    }

    /// Create with custom configuration
    pub fn with_config(pricing: Arc<PricingEngine>, config: MeteringConfig) -> Self {
        let (event_tx, event_rx) = async_channel::bounded(config.buffer_size);

        Self {
            config,
            pricing,
            event_buffer: RwLock::new(Vec::new()),
            current_usage: RwLock::new(HashMap::new()),
            records: RwLock::new(Vec::new()),
            event_tx,
            event_rx,
        }
    }

    /// Record a usage event
    pub async fn record(&self, event: UsageEvent) -> Result<()> {
        // Validate event
        if event.amount <= Decimal::ZERO {
            return Err(EconomicsError::InvalidMeteringEvent(
                "Amount must be positive".to_string(),
            ));
        }

        // Calculate cost for this event
        let cost = self.pricing.calculate_cost(
            event.service_type,
            event.resource_unit,
            event.amount,
        )?;

        // Update current usage
        {
            let mut usage = self.current_usage.write().await;
            let account_usage = usage.entry(event.account_id.clone()).or_insert_with(|| {
                AccountUsage {
                    account_id: event.account_id.clone(),
                    by_service: HashMap::new(),
                    current_period_start: Utc::now(),
                    last_event: None,
                }
            });

            *account_usage
                .by_service
                .entry(event.service_type)
                .or_insert(Decimal::ZERO) += cost;

            account_usage.last_event = Some(event.timestamp);
        }

        // Buffer event for aggregation
        {
            let mut buffer = self.event_buffer.write().await;
            buffer.push(event.clone());

            // Flush if buffer is full
            if buffer.len() >= self.config.buffer_size {
                self.flush_buffer(&mut buffer).await?;
            }
        }

        // Send to event channel for real-time processing
        if self.config.real_time_tracking {
            let _ = self.event_tx.try_send(event);
        }

        Ok(())
    }

    /// Record storage usage
    pub async fn record_storage(&self, account_id: &str, usage: StorageUsage) -> Result<()> {
        // Storage: bytes stored
        if usage.bytes_stored > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Storage,
                ResourceUnit::Bytes,
                Decimal::from(usage.bytes_stored),
            )).await?;
        }

        // Storage: bytes retrieved
        if usage.bytes_retrieved > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Storage,
                ResourceUnit::BandwidthGb,
                Decimal::from(usage.bytes_retrieved) / Decimal::from(1_073_741_824u64), // to GB
            )).await?;
        }

        Ok(())
    }

    /// Record hosting usage
    pub async fn record_hosting(&self, account_id: &str, usage: HostingUsage) -> Result<()> {
        // Bandwidth
        if usage.bandwidth_bytes > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Hosting,
                ResourceUnit::BandwidthGb,
                Decimal::from(usage.bandwidth_bytes) / Decimal::from(1_073_741_824u64),
            )).await?;
        }

        // Custom domains
        if usage.custom_domains > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Hosting,
                ResourceUnit::Domains,
                Decimal::from(usage.custom_domains),
            )).await?;
        }

        Ok(())
    }

    /// Record database usage
    pub async fn record_database(&self, account_id: &str, usage: DatabaseUsage) -> Result<()> {
        // Queries
        if usage.queries > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Database,
                ResourceUnit::Queries,
                Decimal::from(usage.queries),
            )).await?;
        }

        // Vector searches
        if usage.vector_searches > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Database,
                ResourceUnit::VectorSearches,
                Decimal::from(usage.vector_searches),
            )).await?;
        }

        // Storage
        if usage.storage_bytes > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Database,
                ResourceUnit::GbMonth,
                Decimal::from(usage.storage_bytes) / Decimal::from(1_073_741_824u64),
            )).await?;
        }

        Ok(())
    }

    /// Record compute usage
    pub async fn record_compute(&self, account_id: &str, usage: ComputeUsage) -> Result<()> {
        // CPU hours
        if usage.cpu_core_seconds > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::CpuCoreHours,
                Decimal::from(usage.cpu_core_seconds) / Decimal::from(3600),
            )).await?;
        }

        // GPU hours
        if usage.gpu_seconds > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::GpuHours,
                Decimal::from(usage.gpu_seconds) / Decimal::from(3600),
            )).await?;
        }

        // Memory GB hours
        if usage.memory_gb_seconds > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::MemoryGbHours,
                Decimal::from(usage.memory_gb_seconds) / Decimal::from(3600),
            )).await?;
        }

        // Invocations (serverless)
        if usage.invocations > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::Invocations,
                Decimal::from(usage.invocations),
            )).await?;
        }

        Ok(())
    }

    /// Record network usage
    pub async fn record_network(&self, account_id: &str, usage: NetworkUsage) -> Result<()> {
        let total_bytes = usage.ingress_bytes + usage.egress_bytes;
        if total_bytes > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Network,
                ResourceUnit::BandwidthGb,
                Decimal::from(total_bytes) / Decimal::from(1_073_741_824u64),
            )).await?;
        }

        Ok(())
    }

    /// Get current usage for an account
    pub async fn get_account_usage(&self, account_id: &str) -> Result<AccountUsage> {
        let usage = self.current_usage.read().await;
        usage
            .get(account_id)
            .cloned()
            .ok_or_else(|| EconomicsError::AccountNotFound(account_id.to_string()))
    }

    /// Get usage for a specific billing period
    pub async fn get_period_usage(
        &self,
        account_id: &str,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Result<UsageRecord> {
        // Aggregate from buffer and records
        let mut record = UsageRecord::new(account_id, start, end);

        // Check buffered events
        let buffer = self.event_buffer.read().await;
        for event in buffer.iter() {
            if event.account_id == account_id
                && event.timestamp >= start
                && event.timestamp < end
            {
                let cost = self.pricing.calculate_cost(
                    event.service_type,
                    event.resource_unit,
                    event.amount,
                )?;

                *record
                    .by_service
                    .entry(event.service_type)
                    .or_insert(Decimal::ZERO) += cost;

                *record
                    .by_resource
                    .entry(event.resource_unit)
                    .or_insert(Decimal::ZERO) += event.amount;

                record.total_cost += cost;
                record.event_count += 1;
            }
        }

        Ok(record)
    }

    /// Flush event buffer to storage
    async fn flush_buffer(&self, buffer: &mut Vec<UsageEvent>) -> Result<()> {
        if buffer.is_empty() {
            return Ok(());
        }

        tracing::info!("Flushing {} metering events", buffer.len());

        // In production, this would persist to database
        // For now, we just aggregate and clear

        buffer.clear();

        Ok(())
    }

    /// Reset usage for a new billing period
    pub async fn reset_period(&self, account_id: &str) -> Result<()> {
        let mut usage = self.current_usage.write().await;
        if let Some(account_usage) = usage.get_mut(account_id) {
            account_usage.by_service.clear();
            account_usage.current_period_start = Utc::now();
        }
        Ok(())
    }

    /// Get event receiver for real-time processing
    pub fn subscribe(&self) -> async_channel::Receiver<UsageEvent> {
        self.event_rx.clone()
    }

    /// Record compute usage with specific GPU type for differentiated pricing
    pub async fn record_compute_with_gpu(
        &self,
        account_id: &str,
        usage: ComputeUsage,
        gpu_type: Option<GpuType>,
    ) -> Result<()> {
        // CPU hours (unchanged by GPU type)
        if usage.cpu_core_seconds > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::CpuCoreHours,
                Decimal::from(usage.cpu_core_seconds) / Decimal::from(3600),
            ))
            .await?;
        }

        // GPU hours with GPU type multiplier
        if usage.gpu_seconds > 0 {
            let multiplier = gpu_type
                .map(|g| Decimal::from_f64_retain(g.price_multiplier()).unwrap_or(Decimal::ONE))
                .unwrap_or(Decimal::ONE);

            let gpu_type_name = gpu_type
                .map(|g| format!("{:?}", g))
                .unwrap_or_else(|| "Generic".to_string());

            self.record(
                UsageEvent::new(
                    account_id,
                    ServiceType::Compute,
                    ResourceUnit::GpuHours,
                    Decimal::from(usage.gpu_seconds) / Decimal::from(3600) * multiplier,
                )
                .with_metadata("gpu_type", gpu_type_name),
            )
            .await?;
        }

        // Memory GB hours
        if usage.memory_gb_seconds > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::MemoryGbHours,
                Decimal::from(usage.memory_gb_seconds) / Decimal::from(3600),
            ))
            .await?;
        }

        // Invocations (serverless)
        if usage.invocations > 0 {
            self.record(UsageEvent::new(
                account_id,
                ServiceType::Compute,
                ResourceUnit::Invocations,
                Decimal::from(usage.invocations),
            ))
            .await?;
        }

        Ok(())
    }

    /// Get historical usage records for an account
    pub async fn get_historical_records(&self, account_id: &str) -> Vec<UsageRecord> {
        let records = self.records.read().await;
        records
            .iter()
            .filter(|r| r.account_id == account_id)
            .cloned()
            .collect()
    }

    /// Add aggregated record (typically called after period flush)
    pub async fn add_record(&self, record: UsageRecord) {
        let mut records = self.records.write().await;
        records.push(record);
    }

    /// Get metering stats
    pub async fn stats(&self) -> MeteringStats {
        let buffer = self.event_buffer.read().await;
        let usage = self.current_usage.read().await;

        MeteringStats {
            buffer_size: buffer.len(),
            active_accounts: usage.len(),
            total_events_buffered: buffer.len(),
        }
    }
}

/// Metering service statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MeteringStats {
    pub buffer_size: usize,
    pub active_accounts: usize,
    pub total_events_buffered: usize,
}

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

    #[tokio::test]
    async fn test_record_usage_event() {
        let pricing = Arc::new(PricingEngine::new());
        let metering = MeteringService::new(pricing);

        let event = UsageEvent::new(
            "account_123",
            ServiceType::Storage,
            ResourceUnit::Bytes,
            dec!(1000000),
        );

        metering.record(event).await.unwrap();

        let usage = metering.get_account_usage("account_123").await.unwrap();
        assert!(usage.by_service.contains_key(&ServiceType::Storage));
    }

    #[tokio::test]
    async fn test_storage_usage() {
        let pricing = Arc::new(PricingEngine::new());
        let metering = MeteringService::new(pricing);

        let usage = StorageUsage {
            bytes_stored: 1_000_000,
            bytes_retrieved: 500_000,
            deals_created: 1,
        };

        metering.record_storage("account_456", usage).await.unwrap();

        let account_usage = metering.get_account_usage("account_456").await.unwrap();
        assert!(account_usage.by_service.contains_key(&ServiceType::Storage));
    }

    #[tokio::test]
    async fn test_invalid_amount() {
        let pricing = Arc::new(PricingEngine::new());
        let metering = MeteringService::new(pricing);

        let event = UsageEvent::new(
            "account_789",
            ServiceType::Storage,
            ResourceUnit::Bytes,
            dec!(-100), // Invalid: negative
        );

        let result = metering.record(event).await;
        assert!(result.is_err());
    }
}
