use rmcp::schemars::{self, JsonSchema};
use rmcp::{
    handler::server::tool::ToolRouter, handler::server::wrapper::Parameters, model::*, tool,
    tool_handler, tool_router, ErrorData as McpError, ServerHandler,
};
use serde::Deserialize;

use crate::kraken::client::KrakenClient;

// === Request types ===

#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetTickerParams {
    #[schemars(description = "Trading pair (e.g. XBTUSD, ETHUSD, SOLUSD)")]
    pub pair: String,
}

#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetOrderbookParams {
    #[schemars(description = "Trading pair (e.g. XBTUSD, ETHUSD)")]
    pub pair: String,
    #[schemars(description = "Max number of asks/bids (1-500, default 10)")]
    pub count: Option<u32>,
}

#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetOhlcParams {
    #[schemars(description = "Trading pair (e.g. XBTUSD, ETHUSD)")]
    pub pair: String,
    #[schemars(description = "Interval in minutes: 1, 5, 15, 30, 60, 240, 1440, 10080, 21600")]
    pub interval: Option<u32>,
    #[schemars(description = "Return data since UNIX timestamp")]
    pub since: Option<u64>,
}

#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetTradeHistoryParams {
    #[schemars(description = "Result offset for pagination")]
    pub offset: Option<u32>,
}

#[derive(Debug, Deserialize, JsonSchema)]
pub struct PlaceOrderParams {
    #[schemars(description = "Trading pair (e.g. XBTUSD, ETHUSD)")]
    pub pair: String,
    #[schemars(description = "Order direction: 'buy' or 'sell'")]
    pub direction: String,
    #[schemars(description = "Order type: 'market' or 'limit'")]
    pub order_type: String,
    #[schemars(description = "Volume in base currency (e.g. '0.01' for 0.01 BTC)")]
    pub volume: String,
    #[schemars(description = "Limit price (required for limit orders)")]
    pub price: Option<String>,
    #[schemars(description = "If true, validate without placing (dry run). Recommended.")]
    pub validate: Option<bool>,
}

#[derive(Debug, Deserialize, JsonSchema)]
pub struct CancelOrderParams {
    #[schemars(description = "Transaction ID of the order to cancel")]
    pub txid: String,
}

// === Server ===

#[derive(Clone)]
pub struct KrakenMcpServer {
    client: KrakenClient,
    tool_router: ToolRouter<Self>,
}

#[tool_router]
impl KrakenMcpServer {
    pub fn new() -> Self {
        Self {
            client: KrakenClient::from_env(),
            tool_router: Self::tool_router(),
        }
    }

    #[tool(
        name = "get_ticker",
        description = "Get current ticker for a trading pair: ask/bid price, last trade, 24h volume, high/low. No auth required."
    )]
    async fn get_ticker(
        &self,
        Parameters(p): Parameters<GetTickerParams>,
    ) -> Result<CallToolResult, McpError> {
        match self.client.ticker(&p.pair).await {
            Ok(ticker) => Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&ticker).unwrap_or_default(),
            )])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "get_orderbook",
        description = "Get order book (asks/bids) for a trading pair. No auth required."
    )]
    async fn get_orderbook(
        &self,
        Parameters(p): Parameters<GetOrderbookParams>,
    ) -> Result<CallToolResult, McpError> {
        match self.client.orderbook(&p.pair, p.count).await {
            Ok(book) => Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&book).unwrap_or_default(),
            )])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "get_ohlc",
        description = "Get OHLC candlestick data for a trading pair. No auth required."
    )]
    async fn get_ohlc(
        &self,
        Parameters(p): Parameters<GetOhlcParams>,
    ) -> Result<CallToolResult, McpError> {
        match self.client.ohlc(&p.pair, p.interval, p.since).await {
            Ok(data) => Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&data).unwrap_or_default(),
            )])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "get_balance",
        description = "Get all non-zero account balances. Requires KRAKEN_API_KEY and KRAKEN_API_SECRET env vars."
    )]
    async fn get_balance(&self) -> Result<CallToolResult, McpError> {
        match self.client.balance().await {
            Ok(balances) => {
                let non_zero: std::collections::HashMap<_, _> = balances
                    .into_iter()
                    .filter(|(_, v)| v.parse::<f64>().unwrap_or(0.0) != 0.0)
                    .collect();
                Ok(CallToolResult::success(vec![Content::text(
                    serde_json::to_string_pretty(&non_zero).unwrap_or_default(),
                )]))
            }
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "get_trade_history",
        description = "Get recent executed trades with pair, price, volume, cost, fee. Requires API keys."
    )]
    async fn get_trade_history(
        &self,
        Parameters(p): Parameters<GetTradeHistoryParams>,
    ) -> Result<CallToolResult, McpError> {
        match self.client.trade_history(p.offset).await {
            Ok(history) => Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&history.trades).unwrap_or_default(),
            )])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "get_open_orders",
        description = "List all open orders. Requires API keys."
    )]
    async fn get_open_orders(&self) -> Result<CallToolResult, McpError> {
        match self.client.open_orders().await {
            Ok(orders) => Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&orders.open).unwrap_or_default(),
            )])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "place_order",
        description = "⚠️ REAL MONEY — Place a buy/sell order on Kraken. Market orders execute IMMEDIATELY at market price. Use validate=true to dry-run first. ALWAYS confirm with user before placing. Requires API keys."
    )]
    async fn place_order(
        &self,
        Parameters(p): Parameters<PlaceOrderParams>,
    ) -> Result<CallToolResult, McpError> {
        let validate = p.validate.unwrap_or(false);
        match self
            .client
            .add_order(
                &p.pair,
                &p.direction,
                &p.order_type,
                &p.volume,
                p.price.as_deref(),
                validate,
            )
            .await
        {
            Ok(result) => {
                let mut text = if validate {
                    "✅ VALIDATION ONLY (no order placed)\n\n".to_string()
                } else {
                    "🚨 ORDER PLACED — REAL TRADE EXECUTED\n\n".to_string()
                };
                if let Some(descr) = &result.descr {
                    if let Some(order) = &descr.order {
                        text.push_str(&format!("Order: {order}\n"));
                    }
                }
                if let Some(txids) = &result.txid {
                    text.push_str(&format!("Transaction IDs: {}\n", txids.join(", ")));
                }
                Ok(CallToolResult::success(vec![Content::text(text)]))
            }
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }

    #[tool(
        name = "cancel_order",
        description = "Cancel an open order by transaction ID. Requires API keys."
    )]
    async fn cancel_order(
        &self,
        Parameters(p): Parameters<CancelOrderParams>,
    ) -> Result<CallToolResult, McpError> {
        match self.client.cancel_order(&p.txid).await {
            Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
                "Cancelled {} order(s)",
                result.count.unwrap_or(0)
            ))])),
            Err(e) => Ok(CallToolResult::error(vec![Content::text(e.to_string())])),
        }
    }
}

#[tool_handler]
impl ServerHandler for KrakenMcpServer {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            instructions: Some(
                "Kraken cryptocurrency exchange MCP server. Market data tools need no auth. \
                 Account and trading tools require KRAKEN_API_KEY and KRAKEN_API_SECRET env vars. \
                 ⚠️ Trading tools execute REAL orders with REAL money."
                    .into(),
            ),
            capabilities: ServerCapabilities::builder().enable_tools().build(),
            ..Default::default()
        }
    }
}
