//! Spot market and pricing engine for compute resources.
//!
//! Implements real-time pricing based on supply/demand, geographic arbitrage,
//! and auction-based compute allocation for 90% cost reduction vs cloud providers.

use crate::error::ComputeError;
use crate::processor::ProcessorType;
use crate::{NodeId, ProcessorId};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::{BinaryHeap, HashMap};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

/// Unique order identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OrderId(pub u64);

impl OrderId {
    /// Creates a new order ID.
    pub fn new() -> Self {
        use rand::Rng;
        OrderId(rand::thread_rng().gen())
    }
}

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

/// Order side (buy or sell).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum OrderSide {
    /// Buying compute resources.
    Buy,
    /// Selling compute resources.
    Sell,
}

/// Order type.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum OrderType {
    /// Market order - execute at best available price.
    Market,
    /// Limit order - execute only at specified price or better.
    Limit,
    /// Fill or kill - execute entirely or cancel.
    FillOrKill,
    /// Immediate or cancel - execute as much as possible immediately.
    ImmediateOrCancel,
}

/// Resource type being traded.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ResourceType {
    /// GPU compute hours.
    GpuHours(GpuTier),
    /// CPU compute hours.
    CpuHours(CpuTier),
    /// TPU compute hours.
    TpuHours,
    /// NPU compute hours.
    NpuHours,
    /// LPU inference credits.
    LpuCredits,
    /// Memory GB-hours.
    MemoryGbHours,
    /// Network bandwidth GB.
    NetworkGb,
    /// Storage GB-hours.
    StorageGbHours,
}

/// GPU performance tier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GpuTier {
    /// Entry level (RTX 3060, etc.).
    Entry,
    /// Mid-range (RTX 4080, A4000, etc.).
    Mid,
    /// High-end (RTX 4090, A6000, etc.).
    High,
    /// Data center (A100, H100, etc.).
    DataCenter,
    /// Ultra (H100 SXM, B200, etc.).
    Ultra,
}

/// CPU performance tier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CpuTier {
    /// Mobile/ARM.
    Mobile,
    /// Desktop.
    Desktop,
    /// Workstation.
    Workstation,
    /// Server.
    Server,
}

/// A market order for compute resources.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Order {
    /// Unique order ID.
    pub id: OrderId,
    /// Node placing the order.
    pub node_id: NodeId,
    /// Buy or sell.
    pub side: OrderSide,
    /// Order type.
    pub order_type: OrderType,
    /// Resource being traded.
    pub resource: ResourceType,
    /// Quantity in resource units.
    pub quantity: f64,
    /// Price per unit in Synor credits.
    pub price: f64,
    /// Remaining unfilled quantity.
    pub remaining: f64,
    /// Timestamp.
    pub timestamp: u64,
    /// Expiration (None = good till cancelled).
    pub expires_at: Option<u64>,
    /// Geographic region preference.
    pub region: Option<String>,
}

impl Order {
    /// Creates a new order.
    pub fn new(
        node_id: NodeId,
        side: OrderSide,
        order_type: OrderType,
        resource: ResourceType,
        quantity: f64,
        price: f64,
    ) -> Self {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        Self {
            id: OrderId::new(),
            node_id,
            side,
            order_type,
            resource,
            quantity,
            price,
            remaining: quantity,
            timestamp: now,
            expires_at: None,
            region: None,
        }
    }

    /// Sets expiration time.
    pub fn with_expiration(mut self, expires_at: u64) -> Self {
        self.expires_at = Some(expires_at);
        self
    }

    /// Sets region preference.
    pub fn with_region(mut self, region: String) -> Self {
        self.region = Some(region);
        self
    }

    /// Checks if order is expired.
    pub fn is_expired(&self) -> bool {
        if let Some(expires) = self.expires_at {
            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();
            now >= expires
        } else {
            false
        }
    }

    /// Checks if order is fully filled.
    pub fn is_filled(&self) -> bool {
        self.remaining <= 0.0
    }
}

/// Order wrapper for priority queue (bid side - max heap).
#[derive(Clone, Debug)]
struct BidOrder(Arc<RwLock<Order>>);

impl PartialEq for BidOrder {
    fn eq(&self, other: &Self) -> bool {
        let a = self.0.read();
        let b = other.0.read();
        a.price == b.price && a.timestamp == b.timestamp
    }
}

impl Eq for BidOrder {}

impl PartialOrd for BidOrder {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for BidOrder {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        let a = self.0.read();
        let b = other.0.read();
        // Higher price first, then earlier timestamp
        match a.price.partial_cmp(&b.price) {
            Some(std::cmp::Ordering::Equal) => b.timestamp.cmp(&a.timestamp),
            Some(ord) => ord,
            None => std::cmp::Ordering::Equal,
        }
    }
}

/// Order wrapper for priority queue (ask side - min heap).
#[derive(Clone, Debug)]
struct AskOrder(Arc<RwLock<Order>>);

impl PartialEq for AskOrder {
    fn eq(&self, other: &Self) -> bool {
        let a = self.0.read();
        let b = other.0.read();
        a.price == b.price && a.timestamp == b.timestamp
    }
}

impl Eq for AskOrder {}

impl PartialOrd for AskOrder {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for AskOrder {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        let a = self.0.read();
        let b = other.0.read();
        // Lower price first (reverse for min-heap), then earlier timestamp
        match b.price.partial_cmp(&a.price) {
            Some(std::cmp::Ordering::Equal) => b.timestamp.cmp(&a.timestamp),
            Some(ord) => ord,
            None => std::cmp::Ordering::Equal,
        }
    }
}

/// Order book for a single resource type.
pub struct OrderBook {
    /// Resource type this book handles.
    resource: ResourceType,
    /// Buy orders (bids) - max heap.
    bids: RwLock<BinaryHeap<BidOrder>>,
    /// Sell orders (asks) - min heap.
    asks: RwLock<BinaryHeap<AskOrder>>,
    /// Order lookup by ID.
    orders: RwLock<HashMap<OrderId, Arc<RwLock<Order>>>>,
    /// Last traded price.
    last_price: RwLock<Option<f64>>,
    /// Trade history.
    trades: RwLock<Vec<Trade>>,
}

/// A completed trade.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Trade {
    /// Buyer order ID.
    pub buyer_order: OrderId,
    /// Seller order ID.
    pub seller_order: OrderId,
    /// Buyer node.
    pub buyer: NodeId,
    /// Seller node.
    pub seller: NodeId,
    /// Trade price.
    pub price: f64,
    /// Trade quantity.
    pub quantity: f64,
    /// Timestamp.
    pub timestamp: u64,
}

impl OrderBook {
    /// Creates a new order book.
    pub fn new(resource: ResourceType) -> Self {
        Self {
            resource,
            bids: RwLock::new(BinaryHeap::new()),
            asks: RwLock::new(BinaryHeap::new()),
            orders: RwLock::new(HashMap::new()),
            last_price: RwLock::new(None),
            trades: RwLock::new(Vec::new()),
        }
    }

    /// Submits an order and attempts to match.
    pub fn submit(&self, order: Order) -> Result<Vec<Trade>, ComputeError> {
        let order = Arc::new(RwLock::new(order));
        let mut trades = Vec::new();

        // Try to match order
        match order.read().side {
            OrderSide::Buy => {
                trades = self.match_buy(&order);
            }
            OrderSide::Sell => {
                trades = self.match_sell(&order);
            }
        }

        // Add remaining to book if not filled
        let (is_filled, is_ioc, id, side) = {
            let guard = order.read();
            (
                guard.is_filled(),
                matches!(
                    guard.order_type,
                    OrderType::ImmediateOrCancel | OrderType::FillOrKill
                ),
                guard.id,
                guard.side,
            )
        };

        if !is_filled && !is_ioc {
            self.orders.write().insert(id, order.clone());

            match side {
                OrderSide::Buy => {
                    self.bids.write().push(BidOrder(order));
                }
                OrderSide::Sell => {
                    self.asks.write().push(AskOrder(order));
                }
            }
        }

        // Record trades
        if !trades.is_empty() {
            self.trades.write().extend(trades.clone());
            *self.last_price.write() = Some(trades.last().unwrap().price);
        }

        Ok(trades)
    }

    /// Matches a buy order against asks.
    fn match_buy(&self, buy: &Arc<RwLock<Order>>) -> Vec<Trade> {
        let mut trades = Vec::new();
        let mut asks = self.asks.write();

        while !buy.read().is_filled() {
            // Get best ask
            let best_ask = match asks.peek() {
                Some(ask) => ask.clone(),
                None => break,
            };

            let ask_order = best_ask.0.read();

            // Check price compatibility
            if buy.read().price < ask_order.price
                && !matches!(buy.read().order_type, OrderType::Market)
            {
                break;
            }

            // Remove from heap for modification
            drop(ask_order);
            let ask = asks.pop().unwrap().0;

            // Calculate trade
            let mut buy_guard = buy.write();
            let mut ask_guard = ask.write();

            let trade_qty = buy_guard.remaining.min(ask_guard.remaining);
            let trade_price = ask_guard.price; // Use ask price

            buy_guard.remaining -= trade_qty;
            ask_guard.remaining -= trade_qty;

            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();

            trades.push(Trade {
                buyer_order: buy_guard.id,
                seller_order: ask_guard.id,
                buyer: buy_guard.node_id,
                seller: ask_guard.node_id,
                price: trade_price,
                quantity: trade_qty,
                timestamp: now,
            });

            // Put ask back if not filled
            drop(buy_guard);
            drop(ask_guard);

            if !ask.read().is_filled() {
                asks.push(AskOrder(ask));
            }
        }

        trades
    }

    /// Matches a sell order against bids.
    fn match_sell(&self, sell: &Arc<RwLock<Order>>) -> Vec<Trade> {
        let mut trades = Vec::new();
        let mut bids = self.bids.write();

        while !sell.read().is_filled() {
            // Get best bid
            let best_bid = match bids.peek() {
                Some(bid) => bid.clone(),
                None => break,
            };

            let bid_order = best_bid.0.read();

            // Check price compatibility
            if sell.read().price > bid_order.price
                && !matches!(sell.read().order_type, OrderType::Market)
            {
                break;
            }

            // Remove from heap for modification
            drop(bid_order);
            let bid = bids.pop().unwrap().0;

            // Calculate trade
            let mut sell_guard = sell.write();
            let mut bid_guard = bid.write();

            let trade_qty = sell_guard.remaining.min(bid_guard.remaining);
            let trade_price = bid_guard.price; // Use bid price

            sell_guard.remaining -= trade_qty;
            bid_guard.remaining -= trade_qty;

            let now = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();

            trades.push(Trade {
                buyer_order: bid_guard.id,
                seller_order: sell_guard.id,
                buyer: bid_guard.node_id,
                seller: sell_guard.node_id,
                price: trade_price,
                quantity: trade_qty,
                timestamp: now,
            });

            // Put bid back if not filled
            drop(sell_guard);
            drop(bid_guard);

            if !bid.read().is_filled() {
                bids.push(BidOrder(bid));
            }
        }

        trades
    }

    /// Gets the best bid price.
    pub fn best_bid(&self) -> Option<f64> {
        self.bids.read().peek().map(|b| b.0.read().price)
    }

    /// Gets the best ask price.
    pub fn best_ask(&self) -> Option<f64> {
        self.asks.read().peek().map(|a| a.0.read().price)
    }

    /// Gets the bid-ask spread.
    pub fn spread(&self) -> Option<f64> {
        match (self.best_bid(), self.best_ask()) {
            (Some(bid), Some(ask)) => Some(ask - bid),
            _ => None,
        }
    }

    /// Gets the mid price.
    pub fn mid_price(&self) -> Option<f64> {
        match (self.best_bid(), self.best_ask()) {
            (Some(bid), Some(ask)) => Some((bid + ask) / 2.0),
            _ => self.last_price.read().clone(),
        }
    }

    /// Gets last traded price.
    pub fn last_price(&self) -> Option<f64> {
        *self.last_price.read()
    }
}

/// Regional electricity pricing for geographic arbitrage.
#[derive(Clone, Debug)]
pub struct RegionalPricing {
    /// Region identifier.
    pub region: String,
    /// Electricity cost in USD per kWh.
    pub electricity_cost: f64,
    /// Carbon intensity (gCO2/kWh).
    pub carbon_intensity: f64,
    /// Current grid load factor (0.0-1.0).
    pub grid_load: f64,
    /// Renewable energy percentage.
    pub renewable_pct: f64,
}

impl RegionalPricing {
    /// Calculates effective compute cost multiplier.
    pub fn cost_multiplier(&self) -> f64 {
        // Base on electricity cost, with grid load adjustment
        let base = self.electricity_cost / 0.10; // Normalized to $0.10/kWh baseline
        let load_adj = 1.0 + (self.grid_load - 0.5) * 0.2; // ±10% based on load
        base * load_adj
    }
}

/// Default regional pricing data.
pub fn default_regional_pricing() -> Vec<RegionalPricing> {
    vec![
        RegionalPricing {
            region: "us-west".to_string(),
            electricity_cost: 0.12,
            carbon_intensity: 200.0,
            grid_load: 0.6,
            renewable_pct: 0.35,
        },
        RegionalPricing {
            region: "us-east".to_string(),
            electricity_cost: 0.11,
            carbon_intensity: 350.0,
            grid_load: 0.7,
            renewable_pct: 0.15,
        },
        RegionalPricing {
            region: "eu-west".to_string(),
            electricity_cost: 0.25,
            carbon_intensity: 150.0,
            grid_load: 0.5,
            renewable_pct: 0.45,
        },
        RegionalPricing {
            region: "eu-north".to_string(),
            electricity_cost: 0.08,
            carbon_intensity: 50.0,
            grid_load: 0.4,
            renewable_pct: 0.90,
        },
        RegionalPricing {
            region: "asia-east".to_string(),
            electricity_cost: 0.10,
            carbon_intensity: 500.0,
            grid_load: 0.8,
            renewable_pct: 0.20,
        },
        RegionalPricing {
            region: "asia-south".to_string(),
            electricity_cost: 0.07,
            carbon_intensity: 600.0,
            grid_load: 0.6,
            renewable_pct: 0.10,
        },
    ]
}

/// Pricing engine for compute resources.
pub struct PricingEngine {
    /// Base prices per resource type.
    base_prices: HashMap<ResourceType, f64>,
    /// Regional pricing data.
    regions: Vec<RegionalPricing>,
    /// Supply/demand factors.
    supply_demand: RwLock<HashMap<ResourceType, f64>>,
    /// Time-of-day factors.
    time_factors: Vec<f64>, // 24 hourly factors
}

impl PricingEngine {
    /// Creates a new pricing engine.
    pub fn new() -> Self {
        let mut base_prices = HashMap::new();

        // Base prices in Synor credits per unit
        // Designed to be ~90% cheaper than AWS/GCP/Azure
        base_prices.insert(ResourceType::GpuHours(GpuTier::Entry), 0.05);
        base_prices.insert(ResourceType::GpuHours(GpuTier::Mid), 0.15);
        base_prices.insert(ResourceType::GpuHours(GpuTier::High), 0.30);
        base_prices.insert(ResourceType::GpuHours(GpuTier::DataCenter), 0.80);
        base_prices.insert(ResourceType::GpuHours(GpuTier::Ultra), 1.50);
        base_prices.insert(ResourceType::CpuHours(CpuTier::Mobile), 0.001);
        base_prices.insert(ResourceType::CpuHours(CpuTier::Desktop), 0.005);
        base_prices.insert(ResourceType::CpuHours(CpuTier::Workstation), 0.015);
        base_prices.insert(ResourceType::CpuHours(CpuTier::Server), 0.03);
        base_prices.insert(ResourceType::TpuHours, 1.00);
        base_prices.insert(ResourceType::NpuHours, 0.10);
        base_prices.insert(ResourceType::LpuCredits, 0.50);
        base_prices.insert(ResourceType::MemoryGbHours, 0.001);
        base_prices.insert(ResourceType::NetworkGb, 0.01);
        base_prices.insert(ResourceType::StorageGbHours, 0.0001);

        // Time-of-day factors (0 = midnight UTC)
        // Lower prices during off-peak hours
        let time_factors = vec![
            0.7, 0.6, 0.5, 0.5, 0.5, 0.6, // 00:00 - 05:00 (off-peak)
            0.8, 0.9, 1.0, 1.0, 1.0, 1.0, // 06:00 - 11:00 (ramp up)
            1.0, 1.1, 1.2, 1.2, 1.1, 1.0, // 12:00 - 17:00 (peak)
            0.9, 0.9, 0.8, 0.8, 0.7, 0.7, // 18:00 - 23:00 (wind down)
        ];

        Self {
            base_prices,
            regions: default_regional_pricing(),
            supply_demand: RwLock::new(HashMap::new()),
            time_factors,
        }
    }

    /// Gets the current spot price for a resource.
    pub fn spot_price(&self, resource: &ResourceType, region: Option<&str>) -> f64 {
        let base = self.base_prices.get(resource).copied().unwrap_or(0.1);

        // Apply supply/demand factor
        let sd_factor = self
            .supply_demand
            .read()
            .get(resource)
            .copied()
            .unwrap_or(1.0);

        // Apply time-of-day factor
        let hour = chrono::Utc::now().hour() as usize;
        let time_factor = self.time_factors.get(hour).copied().unwrap_or(1.0);

        // Apply regional factor
        let region_factor = region
            .and_then(|r| self.regions.iter().find(|p| p.region == r))
            .map(|p| p.cost_multiplier())
            .unwrap_or(1.0);

        base * sd_factor * time_factor * region_factor
    }

    /// Updates supply/demand factor for a resource.
    pub fn update_supply_demand(&self, resource: ResourceType, supply: f64, demand: f64) {
        // Factor increases when demand > supply
        let factor = if supply > 0.0 {
            (demand / supply).sqrt().clamp(0.5, 2.0)
        } else if demand > 0.0 {
            2.0 // Max factor when no supply
        } else {
            1.0
        };

        self.supply_demand.write().insert(resource, factor);
    }

    /// Gets cheapest region for a resource.
    pub fn cheapest_region(&self, resource: &ResourceType) -> &str {
        self.regions
            .iter()
            .min_by(|a, b| {
                let cost_a = self.spot_price(resource, Some(&a.region));
                let cost_b = self.spot_price(resource, Some(&b.region));
                cost_a.partial_cmp(&cost_b).unwrap()
            })
            .map(|r| r.region.as_str())
            .unwrap_or("us-west")
    }

    /// Gets greenest region for a resource.
    pub fn greenest_region(&self) -> &str {
        self.regions
            .iter()
            .max_by(|a, b| {
                a.renewable_pct
                    .partial_cmp(&b.renewable_pct)
                    .unwrap()
            })
            .map(|r| r.region.as_str())
            .unwrap_or("eu-north")
    }

    /// Compares price to cloud providers.
    pub fn compare_to_cloud(&self, resource: &ResourceType, region: Option<&str>) -> CloudComparison {
        let our_price = self.spot_price(resource, region);

        // Approximate cloud provider prices (USD/hour for GPU)
        let (aws_price, gcp_price, azure_price) = match resource {
            ResourceType::GpuHours(GpuTier::DataCenter) => (3.06, 2.95, 3.10), // A100 equivalents
            ResourceType::GpuHours(GpuTier::Ultra) => (5.00, 4.50, 5.20),       // H100 equivalents
            ResourceType::GpuHours(GpuTier::High) => (1.50, 1.40, 1.60),        // T4/A10 equivalents
            ResourceType::CpuHours(CpuTier::Server) => (0.40, 0.35, 0.42),
            _ => (1.0, 1.0, 1.0),
        };

        CloudComparison {
            synor_price: our_price,
            aws_price,
            gcp_price,
            azure_price,
            aws_savings: ((aws_price - our_price) / aws_price * 100.0).max(0.0),
            gcp_savings: ((gcp_price - our_price) / gcp_price * 100.0).max(0.0),
            azure_savings: ((azure_price - our_price) / azure_price * 100.0).max(0.0),
        }
    }
}

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

/// Comparison with cloud provider prices.
#[derive(Clone, Debug)]
pub struct CloudComparison {
    /// Our spot price.
    pub synor_price: f64,
    /// AWS price.
    pub aws_price: f64,
    /// GCP price.
    pub gcp_price: f64,
    /// Azure price.
    pub azure_price: f64,
    /// Savings vs AWS (percentage).
    pub aws_savings: f64,
    /// Savings vs GCP (percentage).
    pub gcp_savings: f64,
    /// Savings vs Azure (percentage).
    pub azure_savings: f64,
}

/// Spot market for compute resources.
pub struct SpotMarket {
    /// Order books per resource type.
    order_books: HashMap<ResourceType, OrderBook>,
    /// Pricing engine.
    pricing: PricingEngine,
    /// Provider registry (node -> resources offered).
    providers: RwLock<HashMap<NodeId, Vec<ProviderListing>>>,
    /// Active auctions.
    auctions: RwLock<HashMap<AuctionId, Auction>>,
}

/// Unique auction identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AuctionId(pub u64);

impl AuctionId {
    /// Creates a new auction ID.
    pub fn new() -> Self {
        use rand::Rng;
        AuctionId(rand::thread_rng().gen())
    }
}

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

/// A provider's resource listing.
#[derive(Clone, Debug)]
pub struct ProviderListing {
    /// Resource type offered.
    pub resource: ResourceType,
    /// Available quantity.
    pub quantity: f64,
    /// Minimum price accepted.
    pub min_price: f64,
    /// Region.
    pub region: String,
    /// Processor types available.
    pub processors: Vec<ProcessorType>,
}

/// An auction for compute resources.
#[derive(Clone, Debug)]
pub struct Auction {
    /// Auction ID.
    pub id: AuctionId,
    /// Resource being auctioned.
    pub resource: ResourceType,
    /// Quantity needed.
    pub quantity: f64,
    /// Maximum price buyer will pay.
    pub max_price: f64,
    /// Current winning bid.
    pub winning_bid: Option<(NodeId, f64)>,
    /// All bids.
    pub bids: Vec<(NodeId, f64)>,
    /// Start time.
    pub started: Instant,
    /// Duration.
    pub duration: Duration,
    /// Whether auction is closed.
    pub closed: bool,
}

impl Auction {
    /// Creates a new auction.
    pub fn new(resource: ResourceType, quantity: f64, max_price: f64, duration: Duration) -> Self {
        Self {
            id: AuctionId::new(),
            resource,
            quantity,
            max_price,
            winning_bid: None,
            bids: Vec::new(),
            started: Instant::now(),
            duration,
            closed: false,
        }
    }

    /// Submits a bid.
    pub fn bid(&mut self, node: NodeId, price: f64) -> bool {
        if self.closed || price > self.max_price {
            return false;
        }

        self.bids.push((node, price));

        // Update winning bid (lowest price wins)
        if self.winning_bid.is_none() || price < self.winning_bid.unwrap().1 {
            self.winning_bid = Some((node, price));
        }

        true
    }

    /// Checks if auction has ended.
    pub fn is_ended(&self) -> bool {
        self.closed || self.started.elapsed() >= self.duration
    }

    /// Closes the auction.
    pub fn close(&mut self) -> Option<(NodeId, f64)> {
        self.closed = true;
        self.winning_bid
    }
}

impl SpotMarket {
    /// Creates a new spot market.
    pub fn new() -> Self {
        let mut order_books = HashMap::new();

        // Create order books for common resources
        for tier in [
            GpuTier::Entry,
            GpuTier::Mid,
            GpuTier::High,
            GpuTier::DataCenter,
            GpuTier::Ultra,
        ] {
            order_books.insert(
                ResourceType::GpuHours(tier),
                OrderBook::new(ResourceType::GpuHours(tier)),
            );
        }

        for tier in [
            CpuTier::Mobile,
            CpuTier::Desktop,
            CpuTier::Workstation,
            CpuTier::Server,
        ] {
            order_books.insert(
                ResourceType::CpuHours(tier),
                OrderBook::new(ResourceType::CpuHours(tier)),
            );
        }

        order_books.insert(ResourceType::TpuHours, OrderBook::new(ResourceType::TpuHours));
        order_books.insert(ResourceType::NpuHours, OrderBook::new(ResourceType::NpuHours));
        order_books.insert(ResourceType::LpuCredits, OrderBook::new(ResourceType::LpuCredits));

        Self {
            order_books,
            pricing: PricingEngine::new(),
            providers: RwLock::new(HashMap::new()),
            auctions: RwLock::new(HashMap::new()),
        }
    }

    /// Registers a compute provider.
    pub fn register_provider(&self, node_id: NodeId, listings: Vec<ProviderListing>) {
        self.providers.write().insert(node_id, listings);
    }

    /// Submits an order.
    pub fn submit_order(&self, order: Order) -> Result<Vec<Trade>, ComputeError> {
        let book = self.order_books.get(&order.resource).ok_or_else(|| {
            ComputeError::Internal(format!("No order book for resource: {:?}", order.resource))
        })?;

        book.submit(order)
    }

    /// Gets spot price for a resource.
    pub fn spot_price(&self, resource: &ResourceType, region: Option<&str>) -> f64 {
        // Check if there's a market price
        if let Some(book) = self.order_books.get(resource) {
            if let Some(mid) = book.mid_price() {
                return mid;
            }
        }

        // Fall back to pricing engine
        self.pricing.spot_price(resource, region)
    }

    /// Starts an auction for compute resources.
    pub fn start_auction(
        &self,
        resource: ResourceType,
        quantity: f64,
        max_price: f64,
        duration: Duration,
    ) -> AuctionId {
        let auction = Auction::new(resource, quantity, max_price, duration);
        let id = auction.id;
        self.auctions.write().insert(id, auction);
        id
    }

    /// Submits a bid to an auction.
    pub fn bid_auction(&self, auction_id: AuctionId, node: NodeId, price: f64) -> bool {
        if let Some(auction) = self.auctions.write().get_mut(&auction_id) {
            auction.bid(node, price)
        } else {
            false
        }
    }

    /// Closes an auction and returns the winner.
    pub fn close_auction(&self, auction_id: AuctionId) -> Option<(NodeId, f64)> {
        if let Some(auction) = self.auctions.write().get_mut(&auction_id) {
            auction.close()
        } else {
            None
        }
    }

    /// Gets pricing comparison with cloud providers.
    pub fn compare_to_cloud(&self, resource: &ResourceType) -> CloudComparison {
        self.pricing.compare_to_cloud(resource, None)
    }

    /// Gets the cheapest region for a resource.
    pub fn cheapest_region(&self, resource: &ResourceType) -> &str {
        self.pricing.cheapest_region(resource)
    }

    /// Gets all provider listings for a resource.
    pub fn find_providers(&self, resource: &ResourceType) -> Vec<(NodeId, ProviderListing)> {
        self.providers
            .read()
            .iter()
            .flat_map(|(node, listings)| {
                listings
                    .iter()
                    .filter(|l| &l.resource == resource)
                    .map(|l| (*node, l.clone()))
            })
            .collect()
    }

    /// Gets market stats for a resource.
    pub fn market_stats(&self, resource: &ResourceType) -> Option<MarketStats> {
        let book = self.order_books.get(resource)?;

        Some(MarketStats {
            best_bid: book.best_bid(),
            best_ask: book.best_ask(),
            spread: book.spread(),
            mid_price: book.mid_price(),
            last_price: book.last_price(),
        })
    }
}

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

/// Market statistics for a resource.
#[derive(Clone, Debug)]
pub struct MarketStats {
    /// Best bid price.
    pub best_bid: Option<f64>,
    /// Best ask price.
    pub best_ask: Option<f64>,
    /// Bid-ask spread.
    pub spread: Option<f64>,
    /// Mid price.
    pub mid_price: Option<f64>,
    /// Last traded price.
    pub last_price: Option<f64>,
}

use chrono::Timelike;

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

    #[test]
    fn test_order_creation() {
        let order = Order::new(
            NodeId(1),
            OrderSide::Buy,
            OrderType::Limit,
            ResourceType::GpuHours(GpuTier::DataCenter),
            10.0,
            0.80,
        );

        assert_eq!(order.remaining, 10.0);
        assert!(!order.is_filled());
    }

    #[test]
    fn test_order_book_matching() {
        let book = OrderBook::new(ResourceType::GpuHours(GpuTier::High));

        // Add a sell order
        let sell = Order::new(
            NodeId(1),
            OrderSide::Sell,
            OrderType::Limit,
            ResourceType::GpuHours(GpuTier::High),
            5.0,
            0.25,
        );
        book.submit(sell).unwrap();

        // Add a matching buy order
        let buy = Order::new(
            NodeId(2),
            OrderSide::Buy,
            OrderType::Limit,
            ResourceType::GpuHours(GpuTier::High),
            3.0,
            0.30,
        );
        let trades = book.submit(buy).unwrap();

        assert_eq!(trades.len(), 1);
        assert_eq!(trades[0].quantity, 3.0);
        assert_eq!(trades[0].price, 0.25); // Uses ask price
    }

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

        let price = engine.spot_price(&ResourceType::GpuHours(GpuTier::DataCenter), Some("eu-north"));
        assert!(price > 0.0);

        // eu-north should be cheaper (low electricity cost)
        let eu_price = engine.spot_price(&ResourceType::GpuHours(GpuTier::DataCenter), Some("eu-north"));
        let eu_west_price = engine.spot_price(&ResourceType::GpuHours(GpuTier::DataCenter), Some("eu-west"));

        // eu-north has cheaper electricity
        assert!(eu_price < eu_west_price);
    }

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

        let comparison = engine.compare_to_cloud(&ResourceType::GpuHours(GpuTier::DataCenter), None);

        // Should show significant savings
        assert!(comparison.aws_savings > 50.0);
        assert!(comparison.gcp_savings > 50.0);
        assert!(comparison.azure_savings > 50.0);
    }

    #[test]
    fn test_auction() {
        let mut auction = Auction::new(
            ResourceType::GpuHours(GpuTier::Ultra),
            100.0,
            2.0,
            Duration::from_secs(60),
        );

        // Submit bids
        assert!(auction.bid(NodeId(1), 1.8));
        assert!(auction.bid(NodeId(2), 1.5));
        assert!(auction.bid(NodeId(3), 1.7));

        // Price too high
        assert!(!auction.bid(NodeId(4), 2.5));

        // Lowest bid wins
        let winner = auction.close();
        assert_eq!(winner, Some((NodeId(2), 1.5)));
    }

    #[test]
    fn test_spot_market() {
        let market = SpotMarket::new();

        // Register a provider
        market.register_provider(
            NodeId(1),
            vec![ProviderListing {
                resource: ResourceType::GpuHours(GpuTier::High),
                quantity: 100.0,
                min_price: 0.20,
                region: "us-west".to_string(),
                processors: vec![],
            }],
        );

        // Get providers
        let providers = market.find_providers(&ResourceType::GpuHours(GpuTier::High));
        assert_eq!(providers.len(), 1);

        // Get spot price
        let price = market.spot_price(&ResourceType::GpuHours(GpuTier::High), None);
        assert!(price > 0.0);
    }
}
