//! Storage Deals - Economic layer for storage
//!
//! Users pay storage providers to store their data.
//! Deals are registered on L1 for enforcement.

use crate::cid::ContentId;
use serde::{Deserialize, Serialize};

/// Storage deal status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DealStatus {
    /// Awaiting storage node acceptance
    Pending,
    /// Deal active, data being stored
    Active,
    /// Deal completed successfully
    Completed,
    /// Storage node failed proofs
    Failed,
    /// Deal cancelled by user
    Cancelled,
}

/// Storage deal between user and storage provider
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageDeal {
    /// Unique deal ID
    pub deal_id: [u8; 32],
    /// Content being stored
    pub cid: ContentId,
    /// Client address (paying for storage)
    pub client: [u8; 32],
    /// Storage provider node ID
    pub provider: Option<[u8; 32]>,
    /// Content size in bytes
    pub size: u64,
    /// Storage duration (in L1 blocks)
    pub duration: u64,
    /// Price per byte per epoch (in atomic SYNOR units)
    pub price_per_byte_epoch: u64,
    /// Total collateral locked by provider
    pub provider_collateral: u64,
    /// Client deposit
    pub client_deposit: u64,
    /// Deal start block (on L1)
    pub start_block: u64,
    /// Deal end block (on L1)
    pub end_block: u64,
    /// Current status
    pub status: DealStatus,
    /// Replication factor (how many nodes store the data)
    pub replication: u8,
    /// Failed proof count
    pub failed_proofs: u32,
    /// Successful proof count
    pub successful_proofs: u32,
}

impl StorageDeal {
    /// Calculate total cost for the deal
    pub fn total_cost(&self) -> u64 {
        let epochs = self.duration;
        self.size
            .saturating_mul(self.price_per_byte_epoch)
            .saturating_mul(epochs)
            .saturating_mul(self.replication as u64)
    }

    /// Check if deal is active
    pub fn is_active(&self) -> bool {
        self.status == DealStatus::Active
    }

    /// Check if deal has expired
    pub fn is_expired(&self, current_block: u64) -> bool {
        current_block >= self.end_block
    }

    /// Calculate provider reward for successful storage
    pub fn provider_reward(&self) -> u64 {
        // Provider gets paid based on successful proofs
        let total_possible_proofs = self.duration / 100; // Assume proof every 100 blocks
        if total_possible_proofs == 0 {
            return 0;
        }
        
        let success_rate = self.successful_proofs as u64 * 100 / total_possible_proofs;
        self.total_cost() * success_rate / 100
    }
}

/// Request to create a new storage deal
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateDealRequest {
    /// Content to store
    pub cid: ContentId,
    /// Content size
    pub size: u64,
    /// Desired duration (blocks)
    pub duration: u64,
    /// Maximum price willing to pay
    pub max_price_per_byte_epoch: u64,
    /// Desired replication factor
    pub replication: u8,
    /// Preferred regions (optional)
    pub preferred_regions: Vec<String>,
}

/// Storage provider offer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageOffer {
    /// Provider node ID
    pub provider: [u8; 32],
    /// Available capacity (bytes)
    pub available_capacity: u64,
    /// Price per byte per epoch
    pub price_per_byte_epoch: u64,
    /// Minimum deal duration
    pub min_duration: u64,
    /// Maximum deal duration
    pub max_duration: u64,
    /// Regions served
    pub regions: Vec<String>,
    /// Provider stake amount
    pub stake: u64,
    /// Historical uptime percentage
    pub uptime: u8,
    /// Success rate (proofs passed / total)
    pub success_rate: u8,
}

/// Storage market for matching clients with providers
#[derive(Debug, Default)]
pub struct StorageMarket {
    /// Active deals
    pub deals: Vec<StorageDeal>,
    /// Available offers from providers
    pub offers: Vec<StorageOffer>,
}

impl StorageMarket {
    /// Create a new storage market
    pub fn new() -> Self {
        Self::default()
    }

    /// Find best providers for a deal request
    pub fn find_providers(&self, request: &CreateDealRequest) -> Vec<&StorageOffer> {
        self.offers
            .iter()
            .filter(|offer| {
                offer.available_capacity >= request.size
                    && offer.price_per_byte_epoch <= request.max_price_per_byte_epoch
                    && offer.min_duration <= request.duration
                    && offer.max_duration >= request.duration
            })
            .collect()
    }

    /// Add a storage offer
    pub fn add_offer(&mut self, offer: StorageOffer) {
        self.offers.push(offer);
    }

    /// Create a deal from request and accepted offer
    pub fn create_deal(
        &mut self,
        request: CreateDealRequest,
        provider: [u8; 32],
        client: [u8; 32],
        current_block: u64,
    ) -> StorageDeal {
        let deal_id = self.generate_deal_id(&request.cid, &client, current_block);
        
        let offer = self.offers.iter().find(|o| o.provider == provider);
        let price = offer.map(|o| o.price_per_byte_epoch).unwrap_or(0);

        let deal = StorageDeal {
            deal_id,
            cid: request.cid,
            client,
            provider: Some(provider),
            size: request.size,
            duration: request.duration,
            price_per_byte_epoch: price,
            provider_collateral: 0, // Set during acceptance
            client_deposit: 0, // Set during creation
            start_block: current_block,
            end_block: current_block + request.duration,
            status: DealStatus::Pending,
            replication: request.replication,
            failed_proofs: 0,
            successful_proofs: 0,
        };

        self.deals.push(deal.clone());
        deal
    }

    fn generate_deal_id(&self, cid: &ContentId, client: &[u8; 32], block: u64) -> [u8; 32] {
        let mut input = Vec::new();
        input.extend_from_slice(&cid.digest);
        input.extend_from_slice(client);
        input.extend_from_slice(&block.to_le_bytes());
        *blake3::hash(&input).as_bytes()
    }
}

/// Pricing tiers for storage
#[derive(Debug, Clone)]
pub struct PricingTier {
    /// Tier name
    pub name: String,
    /// Price per GB per month (in atomic SYNOR)
    pub price_per_gb_month: u64,
    /// Minimum replication
    pub min_replication: u8,
    /// SLA uptime guarantee in basis points (10000 = 100%)
    pub uptime_sla_bps: u16,
}

impl PricingTier {
    /// Standard tier - basic storage
    pub fn standard() -> Self {
        Self {
            name: "Standard".to_string(),
            price_per_gb_month: 100_000_000, // 0.1 SYNOR per GB/month
            min_replication: 3,
            uptime_sla_bps: 9900, // 99.00%
        }
    }

    /// Premium tier - higher redundancy
    pub fn premium() -> Self {
        Self {
            name: "Premium".to_string(),
            price_per_gb_month: 250_000_000, // 0.25 SYNOR per GB/month
            min_replication: 5,
            uptime_sla_bps: 9990, // 99.90%
        }
    }

    /// Permanent tier - one-time payment for ~20 years
    pub fn permanent() -> Self {
        Self {
            name: "Permanent".to_string(),
            price_per_gb_month: 2_400_000_000, // 2.4 SYNOR per GB (one-time, ~20 years equiv)
            min_replication: 10,
            uptime_sla_bps: 9999, // 99.99%
        }
    }
}

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

    #[test]
    fn test_deal_cost() {
        let deal = StorageDeal {
            deal_id: [0u8; 32],
            cid: ContentId::from_content(b"test"),
            client: [1u8; 32],
            provider: Some([2u8; 32]),
            size: 1_000_000_000, // 1 GB
            duration: 100,
            price_per_byte_epoch: 1,
            provider_collateral: 0,
            client_deposit: 0,
            start_block: 0,
            end_block: 100,
            status: DealStatus::Active,
            replication: 3,
            failed_proofs: 0,
            successful_proofs: 0,
        };

        let cost = deal.total_cost();
        assert_eq!(cost, 1_000_000_000 * 100 * 3);
    }

    #[test]
    fn test_market_find_providers() {
        let mut market = StorageMarket::new();
        
        market.add_offer(StorageOffer {
            provider: [1u8; 32],
            available_capacity: 100_000_000_000, // 100 GB
            price_per_byte_epoch: 1,
            min_duration: 10,
            max_duration: 10000,
            regions: vec!["US".to_string()],
            stake: 1000,
            uptime: 99,
            success_rate: 99,
        });

        let request = CreateDealRequest {
            cid: ContentId::from_content(b"test"),
            size: 1_000_000,
            duration: 100,
            max_price_per_byte_epoch: 2,
            replication: 3,
            preferred_regions: vec![],
        };

        let providers = market.find_providers(&request);
        assert_eq!(providers.len(), 1);
    }

    #[test]
    fn test_pricing_tiers() {
        let standard = PricingTier::standard();
        let premium = PricingTier::premium();
        let permanent = PricingTier::permanent();

        assert!(standard.price_per_gb_month < premium.price_per_gb_month);
        assert!(premium.price_per_gb_month < permanent.price_per_gb_month);
        assert!(standard.min_replication < premium.min_replication);
    }
}
