//! Pricing Engine
//!
//! Service pricing, tier management, and discount system.

mod discounts;
mod tiers;

pub use discounts::{find_best_volume_discount, standard_volume_discounts, Discount, DiscountType};
pub use tiers::{compare_tiers, PricingTier, TierComparison};

use crate::error::{EconomicsError, Result};
use crate::{ResourceUnit, ServiceType, SynorDecimal};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Service pricing configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServicePricing {
    /// Base prices by resource unit
    pub base_prices: HashMap<ResourceUnit, SynorDecimal>,
    /// Minimum billable amount
    pub minimum_charge: SynorDecimal,
    /// Free tier included
    pub free_tier: Option<FreeTier>,
}

/// Free tier allocation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FreeTier {
    /// Free allocation per resource
    pub allocations: HashMap<ResourceUnit, Decimal>,
    /// Monthly reset
    pub monthly_reset: bool,
}

/// Pricing engine managing all pricing logic
pub struct PricingEngine {
    /// Pricing by service type
    service_pricing: HashMap<ServiceType, ServicePricing>,
    /// Available pricing tiers
    tiers: Vec<PricingTier>,
    /// Active discounts
    discounts: Vec<Discount>,
}

impl PricingEngine {
    /// Create a new pricing engine with default prices
    pub fn new() -> Self {
        let mut engine = Self {
            service_pricing: HashMap::new(),
            tiers: Vec::new(),
            discounts: Vec::new(),
        };

        engine.initialize_default_pricing();
        engine.initialize_default_tiers();

        engine
    }

    /// Initialize default service pricing
    fn initialize_default_pricing(&mut self) {
        // Storage L2 pricing
        let mut storage_prices = HashMap::new();
        storage_prices.insert(ResourceUnit::GbMonth, Decimal::new(2, 2)); // 0.02 SYNOR/GB/month
        storage_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(1, 2)); // 0.01 SYNOR/GB retrieval
        storage_prices.insert(ResourceUnit::Bytes, Decimal::new(2, 11)); // For small files

        self.service_pricing.insert(
            ServiceType::Storage,
            ServicePricing {
                base_prices: storage_prices,
                minimum_charge: Decimal::new(1, 4), // 0.0001 SYNOR
                free_tier: Some(FreeTier {
                    allocations: [(ResourceUnit::GbMonth, Decimal::new(5, 1))].into(), // 0.5 GB free
                    monthly_reset: true,
                }),
            },
        );

        // Hosting pricing
        let mut hosting_prices = HashMap::new();
        hosting_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(5, 2)); // 0.05 SYNOR/GB
        hosting_prices.insert(ResourceUnit::Domains, Decimal::new(50, 2)); // 0.50 SYNOR/domain/month

        self.service_pricing.insert(
            ServiceType::Hosting,
            ServicePricing {
                base_prices: hosting_prices,
                minimum_charge: Decimal::ZERO,
                free_tier: Some(FreeTier {
                    allocations: [
                        (ResourceUnit::BandwidthGb, Decimal::new(1, 0)), // 1 GB free
                    ]
                    .into(),
                    monthly_reset: true,
                }),
            },
        );

        // Database L2 pricing
        let mut db_prices = HashMap::new();
        db_prices.insert(ResourceUnit::GbMonth, Decimal::new(10, 2)); // 0.10 SYNOR/GB/month
        db_prices.insert(ResourceUnit::Queries, Decimal::new(1, 8)); // 0.00000001 SYNOR/query (0.01/million)
        db_prices.insert(ResourceUnit::VectorSearches, Decimal::new(5, 8)); // 0.00000005 SYNOR/search (0.05/million)

        self.service_pricing.insert(
            ServiceType::Database,
            ServicePricing {
                base_prices: db_prices,
                minimum_charge: Decimal::new(1, 4),
                free_tier: Some(FreeTier {
                    allocations: [
                        (ResourceUnit::GbMonth, Decimal::new(5, 1)), // 0.5 GB free
                        (ResourceUnit::Queries, Decimal::new(1_000_000, 0)), // 1M queries free
                        (ResourceUnit::VectorSearches, Decimal::new(100_000, 0)), // 100K vector searches free
                    ]
                    .into(),
                    monthly_reset: true,
                }),
            },
        );

        // Compute L2 pricing
        let mut compute_prices = HashMap::new();
        compute_prices.insert(ResourceUnit::CpuCoreHours, Decimal::new(2, 2)); // 0.02 SYNOR/core-hour
        compute_prices.insert(ResourceUnit::GpuHours, Decimal::new(50, 2)); // 0.50 SYNOR/GPU-hour (RTX 4090)
        compute_prices.insert(ResourceUnit::MemoryGbHours, Decimal::new(5, 3)); // 0.005 SYNOR/GB-hour
        compute_prices.insert(ResourceUnit::Invocations, Decimal::new(2, 7)); // 0.0000002 SYNOR/invocation (0.20/million)

        self.service_pricing.insert(
            ServiceType::Compute,
            ServicePricing {
                base_prices: compute_prices,
                minimum_charge: Decimal::new(1, 4),
                free_tier: Some(FreeTier {
                    allocations: [
                        (ResourceUnit::CpuCoreHours, Decimal::new(100, 0)), // 100 core-hours free
                        (ResourceUnit::MemoryGbHours, Decimal::new(200, 0)), // 200 GB-hours free
                        (ResourceUnit::Invocations, Decimal::new(1_000_000, 0)), // 1M invocations free
                    ]
                    .into(),
                    monthly_reset: true,
                }),
            },
        );

        // Network pricing
        let mut network_prices = HashMap::new();
        network_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(1, 2)); // 0.01 SYNOR/GB

        self.service_pricing.insert(
            ServiceType::Network,
            ServicePricing {
                base_prices: network_prices,
                minimum_charge: Decimal::ZERO,
                free_tier: Some(FreeTier {
                    allocations: [(ResourceUnit::BandwidthGb, Decimal::new(10, 0))].into(), // 10 GB free
                    monthly_reset: true,
                }),
            },
        );

        // Contract execution (gas pricing)
        let mut contract_prices = HashMap::new();
        contract_prices.insert(ResourceUnit::Gas, Decimal::new(1, 9)); // 0.000000001 SYNOR/gas

        self.service_pricing.insert(
            ServiceType::Contract,
            ServicePricing {
                base_prices: contract_prices,
                minimum_charge: Decimal::ZERO,
                free_tier: None,
            },
        );
    }

    /// Initialize default pricing tiers
    fn initialize_default_tiers(&mut self) {
        self.tiers = vec![
            PricingTier::free(),
            PricingTier::standard(),
            PricingTier::premium(),
            PricingTier::enterprise(),
        ];
    }

    /// Calculate cost for resource usage
    pub fn calculate_cost(
        &self,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
        amount: Decimal,
    ) -> Result<SynorDecimal> {
        let pricing = self
            .service_pricing
            .get(&service_type)
            .ok_or_else(|| EconomicsError::ServiceNotConfigured(service_type.to_string()))?;

        let unit_price = pricing
            .base_prices
            .get(&resource_unit)
            .ok_or_else(|| {
                EconomicsError::ServiceNotConfigured(format!(
                    "{} - {}",
                    service_type, resource_unit
                ))
            })?;

        let cost = amount * unit_price;

        // Apply minimum charge
        Ok(cost.max(pricing.minimum_charge))
    }

    /// Calculate cost with tier discount applied
    pub fn calculate_cost_with_tier(
        &self,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
        amount: Decimal,
        tier_name: &str,
    ) -> Result<SynorDecimal> {
        let base_cost = self.calculate_cost(service_type, resource_unit, amount)?;

        let tier = self.tiers.iter().find(|t| t.name == tier_name);

        if let Some(tier) = tier {
            let discount_rate = tier.discount_percentage / Decimal::ONE_HUNDRED;
            Ok(base_cost * (Decimal::ONE - discount_rate))
        } else {
            Ok(base_cost)
        }
    }

    /// Calculate tier discount amount
    pub fn calculate_tier_discount(
        &self,
        tier_name: &str,
        subtotal: SynorDecimal,
    ) -> Result<SynorDecimal> {
        let tier = self.tiers.iter().find(|t| t.name == tier_name);

        if let Some(tier) = tier {
            let discount_rate = tier.discount_percentage / Decimal::ONE_HUNDRED;
            Ok(subtotal * discount_rate)
        } else {
            Ok(Decimal::ZERO)
        }
    }

    /// Get free tier allocation for a resource
    pub fn get_free_allocation(
        &self,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
    ) -> Decimal {
        self.service_pricing
            .get(&service_type)
            .and_then(|p| p.free_tier.as_ref())
            .and_then(|f| f.allocations.get(&resource_unit))
            .copied()
            .unwrap_or(Decimal::ZERO)
    }

    /// Get base price for a resource
    pub fn get_base_price(
        &self,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
    ) -> Option<SynorDecimal> {
        self.service_pricing
            .get(&service_type)
            .and_then(|p| p.base_prices.get(&resource_unit))
            .copied()
    }

    /// Get pricing tier by name
    pub fn get_tier(&self, name: &str) -> Option<&PricingTier> {
        self.tiers.iter().find(|t| t.name == name)
    }

    /// Get all tiers
    pub fn get_all_tiers(&self) -> &[PricingTier] {
        &self.tiers
    }

    /// Add a custom discount
    pub fn add_discount(&mut self, discount: Discount) {
        self.discounts.push(discount);
    }

    /// Apply applicable discounts to an amount
    pub fn apply_discounts(
        &self,
        amount: SynorDecimal,
        account_id: &str,
        service_type: Option<ServiceType>,
    ) -> (SynorDecimal, Vec<Discount>) {
        let mut discounted = amount;
        let mut applied = Vec::new();

        for discount in &self.discounts {
            if discount.is_applicable(account_id, service_type) {
                let discount_amount = discount.calculate_discount(discounted);
                discounted -= discount_amount;
                applied.push(discount.clone());
            }
        }

        (discounted, applied)
    }

    /// Get volume discount for an amount
    /// Returns the best applicable volume discount based on spending amount
    pub fn get_volume_discount(&self, amount: SynorDecimal) -> Option<Discount> {
        find_best_volume_discount(amount)
    }

    /// Get all standard volume discounts
    pub fn get_volume_discount_tiers(&self) -> Vec<Discount> {
        standard_volume_discounts()
    }

    /// Compare current tier with alternatives based on monthly usage
    /// Returns a recommendation if switching tiers would save money
    pub fn recommend_tier(
        &self,
        current_tier_name: &str,
        monthly_usage: SynorDecimal,
    ) -> Option<TierComparison> {
        let current = self.tiers.iter().find(|t| t.name == current_tier_name)?;
        compare_tiers(current, &self.tiers, monthly_usage)
    }

    /// Calculate cost with automatic volume discount
    pub fn calculate_cost_with_volume_discount(
        &self,
        service_type: ServiceType,
        resource_unit: ResourceUnit,
        amount: Decimal,
    ) -> Result<(SynorDecimal, Option<Discount>)> {
        let base_cost = self.calculate_cost(service_type, resource_unit, amount)?;

        if let Some(discount) = self.get_volume_discount(base_cost) {
            let discount_amount = discount.calculate_discount(base_cost);
            Ok((base_cost - discount_amount, Some(discount)))
        } else {
            Ok((base_cost, None))
        }
    }

    /// Get pricing summary for display
    pub fn get_pricing_summary(&self) -> PricingSummary {
        PricingSummary {
            storage: ServicePricingSummary {
                gb_month: self.get_base_price(ServiceType::Storage, ResourceUnit::GbMonth),
                retrieval_gb: self.get_base_price(ServiceType::Storage, ResourceUnit::BandwidthGb),
                free_storage_gb: self.get_free_allocation(ServiceType::Storage, ResourceUnit::GbMonth),
            },
            hosting: HostingPricingSummary {
                bandwidth_gb: self.get_base_price(ServiceType::Hosting, ResourceUnit::BandwidthGb),
                domain_month: self.get_base_price(ServiceType::Hosting, ResourceUnit::Domains),
                free_bandwidth_gb: self
                    .get_free_allocation(ServiceType::Hosting, ResourceUnit::BandwidthGb),
            },
            database: DatabasePricingSummary {
                storage_gb_month: self.get_base_price(ServiceType::Database, ResourceUnit::GbMonth),
                queries_million: self
                    .get_base_price(ServiceType::Database, ResourceUnit::Queries)
                    .map(|p| p * Decimal::from(1_000_000)),
                vector_searches_million: self
                    .get_base_price(ServiceType::Database, ResourceUnit::VectorSearches)
                    .map(|p| p * Decimal::from(1_000_000)),
                free_queries: self
                    .get_free_allocation(ServiceType::Database, ResourceUnit::Queries),
            },
            compute: ComputePricingSummary {
                cpu_core_hour: self.get_base_price(ServiceType::Compute, ResourceUnit::CpuCoreHours),
                gpu_hour: self.get_base_price(ServiceType::Compute, ResourceUnit::GpuHours),
                memory_gb_hour: self
                    .get_base_price(ServiceType::Compute, ResourceUnit::MemoryGbHours),
                invocations_million: self
                    .get_base_price(ServiceType::Compute, ResourceUnit::Invocations)
                    .map(|p| p * Decimal::from(1_000_000)),
                free_cpu_hours: self
                    .get_free_allocation(ServiceType::Compute, ResourceUnit::CpuCoreHours),
            },
        }
    }
}

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

/// Pricing summary for display
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PricingSummary {
    pub storage: ServicePricingSummary,
    pub hosting: HostingPricingSummary,
    pub database: DatabasePricingSummary,
    pub compute: ComputePricingSummary,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServicePricingSummary {
    pub gb_month: Option<SynorDecimal>,
    pub retrieval_gb: Option<SynorDecimal>,
    pub free_storage_gb: SynorDecimal,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HostingPricingSummary {
    pub bandwidth_gb: Option<SynorDecimal>,
    pub domain_month: Option<SynorDecimal>,
    pub free_bandwidth_gb: SynorDecimal,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabasePricingSummary {
    pub storage_gb_month: Option<SynorDecimal>,
    pub queries_million: Option<SynorDecimal>,
    pub vector_searches_million: Option<SynorDecimal>,
    pub free_queries: SynorDecimal,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComputePricingSummary {
    pub cpu_core_hour: Option<SynorDecimal>,
    pub gpu_hour: Option<SynorDecimal>,
    pub memory_gb_hour: Option<SynorDecimal>,
    pub invocations_million: Option<SynorDecimal>,
    pub free_cpu_hours: SynorDecimal,
}

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

    #[test]
    fn test_storage_pricing() {
        let engine = PricingEngine::new();

        // 10 GB storage for a month
        let cost = engine
            .calculate_cost(ServiceType::Storage, ResourceUnit::GbMonth, dec!(10))
            .unwrap();

        assert_eq!(cost, dec!(0.20)); // 10 * 0.02
    }

    #[test]
    fn test_compute_pricing() {
        let engine = PricingEngine::new();

        // 5 GPU hours
        let cost = engine
            .calculate_cost(ServiceType::Compute, ResourceUnit::GpuHours, dec!(5))
            .unwrap();

        assert_eq!(cost, dec!(2.50)); // 5 * 0.50
    }

    #[test]
    fn test_database_queries() {
        let engine = PricingEngine::new();

        // 10 million queries
        let cost = engine
            .calculate_cost(ServiceType::Database, ResourceUnit::Queries, dec!(10_000_000))
            .unwrap();

        assert_eq!(cost, dec!(0.10)); // 10M * 0.00000001
    }

    #[test]
    fn test_tier_discount() {
        let engine = PricingEngine::new();

        // Premium tier gets 20% discount
        let base_cost = dec!(100);
        let discount = engine.calculate_tier_discount("premium", base_cost).unwrap();

        assert_eq!(discount, dec!(20)); // 20%
    }

    #[test]
    fn test_free_allocation() {
        let engine = PricingEngine::new();

        let free_storage = engine.get_free_allocation(ServiceType::Storage, ResourceUnit::GbMonth);
        assert_eq!(free_storage, dec!(0.5));

        let free_queries = engine.get_free_allocation(ServiceType::Database, ResourceUnit::Queries);
        assert_eq!(free_queries, dec!(1_000_000));
    }
}
