//! # Synor Economics
//!
//! Economics, pricing, metering, and billing infrastructure for Synor L2 services.
//!
//! ## Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────┐
//! │                   SYNOR ECONOMICS LAYER                      │
//! ├─────────────────────────────────────────────────────────────┤
//! │  Billing Engine: Invoice → Payment → Credit                  │
//! │  Metering: Storage | Hosting | Database | Compute | Network  │
//! │  Pricing: Oracle → TWAP → Tiers → Discounts                  │
//! └─────────────────────────────────────────────────────────────┘
//! ```
//!
//! ## Components
//!
//! - **Pricing Oracle**: Aggregates SYNOR/USD prices from multiple sources
//! - **Metering Service**: Tracks real-time resource usage across L2 services
//! - **Billing Engine**: Generates invoices, processes payments, manages credits
//! - **Cost Calculator**: Estimates costs before resource consumption

pub mod billing;
pub mod calculator;
pub mod error;
pub mod metering;
pub mod oracle;
pub mod pricing;

pub use billing::{BillingEngine, Credit, Invoice, InvoiceStatus, Payment, PaymentMethod};
pub use calculator::{CostEstimate, CostEstimator, UsageProjection};
pub use error::{EconomicsError, Result};
pub use metering::{
    ComputeUsage, DatabaseUsage, HostingUsage, MeteringService, NetworkUsage, StorageUsage,
    UsageEvent, UsageRecord,
};
pub use oracle::{
    AggregateFeed, ChainlinkFeed, CoinGeckoFeed, InternalDexFeed, MockPriceFeed, OracleFactory,
    PriceFeed, PriceOracle, PriceOracleBuilder, PriceSource, ProductionOracleConfig, TokenPrice,
};
pub use pricing::{Discount, DiscountType, PricingEngine, PricingTier, ServicePricing};

use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

/// Synor token amount in smallest unit (1 SYNOR = 10^18 units)
pub type SynorAmount = u128;

/// SYNOR decimal representation (for UI display)
pub type SynorDecimal = Decimal;

/// Account identifier
pub type AccountId = String;

/// Service identifier
pub type ServiceId = String;

/// Fee distribution configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeeDistribution {
    /// Percentage burned (deflationary)
    pub burn_rate: Decimal,
    /// Percentage to stakers
    pub staker_rate: Decimal,
    /// Percentage to community treasury
    pub treasury_rate: Decimal,
    /// Percentage to validators/node operators
    pub operator_rate: Decimal,
}

impl Default for FeeDistribution {
    fn default() -> Self {
        Self::transaction_fees()
    }
}

impl FeeDistribution {
    /// Transaction fee distribution (L1)
    pub fn transaction_fees() -> Self {
        Self {
            burn_rate: Decimal::new(10, 2),     // 10%
            staker_rate: Decimal::new(60, 2),   // 60%
            treasury_rate: Decimal::new(20, 2), // 20%
            operator_rate: Decimal::new(10, 2), // 10%
        }
    }

    /// L2 service fee distribution
    pub fn l2_service_fees() -> Self {
        Self {
            burn_rate: Decimal::new(10, 2),     // 10%
            staker_rate: Decimal::ZERO,         // 0%
            treasury_rate: Decimal::new(20, 2), // 20%
            operator_rate: Decimal::new(70, 2), // 70%
        }
    }

    /// Calculate distribution amounts from a total fee
    pub fn distribute(&self, total: SynorDecimal) -> FeeBreakdown {
        FeeBreakdown {
            burn: total * self.burn_rate,
            stakers: total * self.staker_rate,
            treasury: total * self.treasury_rate,
            operators: total * self.operator_rate,
            total,
        }
    }

    /// Validate that rates sum to 100%
    pub fn validate(&self) -> Result<()> {
        let sum = self.burn_rate + self.staker_rate + self.treasury_rate + self.operator_rate;
        if sum != Decimal::ONE {
            return Err(EconomicsError::InvalidFeeDistribution(format!(
                "Rates must sum to 100%, got {}%",
                sum * Decimal::ONE_HUNDRED
            )));
        }
        Ok(())
    }
}

/// Breakdown of fee distribution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeeBreakdown {
    pub burn: SynorDecimal,
    pub stakers: SynorDecimal,
    pub treasury: SynorDecimal,
    pub operators: SynorDecimal,
    pub total: SynorDecimal,
}

/// Service type for metering and billing
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ServiceType {
    /// Storage L2
    Storage,
    /// Web hosting
    Hosting,
    /// Database L2
    Database,
    /// Compute L2 (CPU/GPU/TPU)
    Compute,
    /// Network bandwidth
    Network,
    /// Smart contract execution
    Contract,
}

impl ServiceType {
    /// Get the fee distribution model for this service type
    pub fn fee_distribution(&self) -> FeeDistribution {
        match self {
            ServiceType::Contract => FeeDistribution::transaction_fees(),
            _ => FeeDistribution::l2_service_fees(),
        }
    }
}

impl std::fmt::Display for ServiceType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ServiceType::Storage => write!(f, "storage"),
            ServiceType::Hosting => write!(f, "hosting"),
            ServiceType::Database => write!(f, "database"),
            ServiceType::Compute => write!(f, "compute"),
            ServiceType::Network => write!(f, "network"),
            ServiceType::Contract => write!(f, "contract"),
        }
    }
}

/// Resource unit for pricing and metering
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceUnit {
    /// Storage: bytes
    Bytes,
    /// Storage: gigabytes per month
    GbMonth,
    /// Database: queries
    Queries,
    /// Database: vector searches
    VectorSearches,
    /// Compute: CPU core hours
    CpuCoreHours,
    /// Compute: GPU hours
    GpuHours,
    /// Compute: memory GB hours
    MemoryGbHours,
    /// Compute: serverless invocations
    Invocations,
    /// Network: bandwidth in GB
    BandwidthGb,
    /// Hosting: custom domains
    Domains,
    /// Contract: gas units
    Gas,
}

impl std::fmt::Display for ResourceUnit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ResourceUnit::Bytes => write!(f, "bytes"),
            ResourceUnit::GbMonth => write!(f, "GB/month"),
            ResourceUnit::Queries => write!(f, "queries"),
            ResourceUnit::VectorSearches => write!(f, "vector searches"),
            ResourceUnit::CpuCoreHours => write!(f, "CPU core hours"),
            ResourceUnit::GpuHours => write!(f, "GPU hours"),
            ResourceUnit::MemoryGbHours => write!(f, "memory GB hours"),
            ResourceUnit::Invocations => write!(f, "invocations"),
            ResourceUnit::BandwidthGb => write!(f, "GB bandwidth"),
            ResourceUnit::Domains => write!(f, "domains"),
            ResourceUnit::Gas => write!(f, "gas"),
        }
    }
}

/// Account balance and usage summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountSummary {
    pub account_id: AccountId,
    pub balance: SynorDecimal,
    pub credit_balance: SynorDecimal,
    pub prepaid_balance: SynorDecimal,
    pub current_period_usage: HashMap<ServiceType, SynorDecimal>,
    pub current_period_cost: SynorDecimal,
    pub billing_tier: String,
    pub last_payment: Option<DateTime<Utc>>,
    pub next_invoice_date: Option<DateTime<Utc>>,
}

/// Unified economics manager combining all services
pub struct EconomicsManager {
    pub oracle: Arc<RwLock<PriceOracle>>,
    pub metering: Arc<MeteringService>,
    pub billing: Arc<BillingEngine>,
    pub pricing: Arc<PricingEngine>,
    pub calculator: Arc<CostEstimator>,
}

impl EconomicsManager {
    /// Create a new economics manager with default configuration
    /// Uses a development oracle with mock price feeds
    pub fn new() -> Self {
        use rust_decimal_macros::dec;

        // Default to development oracle with mock feeds at $1.50 base price
        let oracle = Arc::new(RwLock::new(
            oracle::OracleFactory::development(dec!(1.50))
        ));
        let pricing = Arc::new(PricingEngine::new());
        let metering = Arc::new(MeteringService::new(pricing.clone()));
        let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone()));
        let calculator = Arc::new(CostEstimator::new(pricing.clone()));

        Self {
            oracle,
            metering,
            billing,
            pricing,
            calculator,
        }
    }

    /// Create an economics manager with production oracle configuration
    pub fn with_production_oracle(config: oracle::ProductionOracleConfig) -> Self {
        let oracle = Arc::new(RwLock::new(
            oracle::OracleFactory::production(config)
        ));
        let pricing = Arc::new(PricingEngine::new());
        let metering = Arc::new(MeteringService::new(pricing.clone()));
        let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone()));
        let calculator = Arc::new(CostEstimator::new(pricing.clone()));

        Self {
            oracle,
            metering,
            billing,
            pricing,
            calculator,
        }
    }

    /// Create an economics manager with a custom oracle
    pub fn with_oracle(oracle: PriceOracle) -> Self {
        let oracle = Arc::new(RwLock::new(oracle));
        let pricing = Arc::new(PricingEngine::new());
        let metering = Arc::new(MeteringService::new(pricing.clone()));
        let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone()));
        let calculator = Arc::new(CostEstimator::new(pricing.clone()));

        Self {
            oracle,
            metering,
            billing,
            pricing,
            calculator,
        }
    }

    /// Get account summary including balance, usage, and billing info
    pub async fn get_account_summary(&self, account_id: &str) -> Result<AccountSummary> {
        let usage = self.metering.get_account_usage(account_id).await?;
        let billing_info = self.billing.get_account_info(account_id).await?;

        let mut current_period_usage = HashMap::new();
        let mut current_period_cost = Decimal::ZERO;

        for (service_type, amount) in usage.by_service.iter() {
            current_period_usage.insert(*service_type, *amount);
            current_period_cost += *amount;
        }

        Ok(AccountSummary {
            account_id: account_id.to_string(),
            balance: billing_info.balance,
            credit_balance: billing_info.credit_balance,
            prepaid_balance: billing_info.prepaid_balance,
            current_period_usage,
            current_period_cost,
            billing_tier: billing_info.tier_name,
            last_payment: billing_info.last_payment,
            next_invoice_date: billing_info.next_invoice,
        })
    }

    /// Estimate cost for a usage projection
    pub async fn estimate_cost(&self, projection: UsageProjection) -> Result<CostEstimate> {
        self.calculator.estimate(projection).await
    }

    /// Record usage event
    pub async fn record_usage(&self, event: UsageEvent) -> Result<()> {
        self.metering.record(event).await
    }

    /// Update price from external feed
    pub async fn update_price(&self, price: TokenPrice) -> Result<()> {
        let mut oracle = self.oracle.write().await;
        oracle.update_price(price)
    }

    /// Get current SYNOR/USD price
    pub async fn get_synor_price(&self) -> Result<SynorDecimal> {
        let oracle = self.oracle.read().await;
        oracle.get_price("SYNOR", "USD")
    }

    /// Generate invoice for account
    pub async fn generate_invoice(&self, account_id: &str) -> Result<Invoice> {
        self.billing.generate_invoice(account_id).await
    }

    /// Process payment
    pub async fn process_payment(&self, payment: Payment) -> Result<()> {
        self.billing.process_payment(payment).await
    }
}

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

/// Convert raw amount to SYNOR decimal (18 decimals)
pub fn to_synor_decimal(raw: SynorAmount) -> SynorDecimal {
    Decimal::from(raw) / Decimal::from(10u128.pow(18))
}

/// Convert SYNOR decimal to raw amount
pub fn from_synor_decimal(decimal: SynorDecimal) -> SynorAmount {
    let scaled = decimal * Decimal::from(10u128.pow(18));
    scaled.try_into().unwrap_or(0)
}

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

    #[test]
    fn test_fee_distribution_transaction() {
        let dist = FeeDistribution::transaction_fees();
        assert!(dist.validate().is_ok());

        let breakdown = dist.distribute(dec!(100));
        assert_eq!(breakdown.burn, dec!(10));
        assert_eq!(breakdown.stakers, dec!(60));
        assert_eq!(breakdown.treasury, dec!(20));
        assert_eq!(breakdown.operators, dec!(10));
    }

    #[test]
    fn test_fee_distribution_l2() {
        let dist = FeeDistribution::l2_service_fees();
        assert!(dist.validate().is_ok());

        let breakdown = dist.distribute(dec!(100));
        assert_eq!(breakdown.burn, dec!(10));
        assert_eq!(breakdown.stakers, dec!(0));
        assert_eq!(breakdown.treasury, dec!(20));
        assert_eq!(breakdown.operators, dec!(70));
    }

    #[test]
    fn test_synor_decimal_conversion() {
        // 1 SYNOR = 10^18 units
        let one_synor = 1_000_000_000_000_000_000u128;
        let decimal = to_synor_decimal(one_synor);
        assert_eq!(decimal, Decimal::ONE);

        let back = from_synor_decimal(decimal);
        assert_eq!(back, one_synor);
    }

    #[test]
    fn test_invalid_fee_distribution() {
        let invalid = FeeDistribution {
            burn_rate: dec!(0.50),
            staker_rate: dec!(0.50),
            treasury_rate: dec!(0.10),
            operator_rate: dec!(0.10),
        };
        assert!(invalid.validate().is_err());
    }
}
