pub mod alpaca;
pub mod book_sim;
pub mod coinbase;
pub mod ibkr;
pub mod kalshi;
pub mod limitless;
pub mod paper;
pub mod polymarket;
pub mod robinhood;

use crate::error::HorizonError;
use crate::types::{Fill, OrderRequest, Position};

/// Exchange abstraction. All exchanges implement this trait.
pub trait Exchange: Send + Sync {
    /// Submit an order. Returns the exchange-assigned order ID.
    fn submit_order(&self, req: &OrderRequest) -> Result<String, HorizonError>;

    /// Cancel an order by ID.
    fn cancel_order(&self, order_id: &str) -> Result<(), HorizonError>;

    /// Cancel all orders. Returns the number of orders canceled.
    fn cancel_all(&self) -> Result<usize, HorizonError>;

    /// Cancel all orders for a specific market. Returns the number canceled.
    fn cancel_for_market(&self, market_id: &str) -> Result<usize, HorizonError>;

    /// Drain pending fills (call after tick or periodically).
    fn drain_fills(&self) -> Vec<Fill>;

    /// Get the exchange name.
    fn name(&self) -> &str;

    /// Fetch current positions from the exchange for reconciliation.
    /// Returns empty vec for exchanges that don't support this (e.g., paper).
    fn fetch_positions(&self) -> Result<Vec<Position>, HorizonError> {
        Ok(Vec::new())
    }

    /// Amend an order's price and/or size. Default: cancel + resubmit.
    /// Returns the (possibly new) order ID.
    fn amend_order(
        &self,
        order_id: &str,
        new_price: Option<f64>,
        new_size: Option<f64>,
        original_request: &OrderRequest,
    ) -> Result<String, HorizonError> {
        // Validate new parameters BEFORE canceling to avoid losing the order
        if let Some(p) = new_price {
            if !p.is_finite() || p < 0.0 {
                return Err(HorizonError::Order(format!(
                    "amend_order: invalid new_price {}", p
                )));
            }
        }
        if let Some(s) = new_size {
            if !s.is_finite() || s <= 0.0 {
                return Err(HorizonError::Order(format!(
                    "amend_order: invalid new_size {}", s
                )));
            }
        }
        self.cancel_order(order_id)?;
        let mut req = original_request.clone();
        if let Some(p) = new_price {
            req.price = p;
        }
        if let Some(s) = new_size {
            req.size = s;
        }
        self.submit_order(&req)
    }
}
