//! `fxt trade` -- order management.

use anyhow::Result;
use clap::{Parser, Subcommand};
use colored::Colorize;
use serde::Serialize;

use crate::grpc::FoxhuntClient;
use crate::output::{self, HumanReadable, OutputFormat};
use crate::proto::trading;

#[derive(Parser, Debug)]
pub struct TradeCommand {
    #[command(subcommand)]
    action: TradeAction,
}

#[derive(Subcommand, Debug)]
enum TradeAction {
    /// Submit a new order
    Submit {
        /// Trading symbol (e.g. ES.FUT)
        #[arg(long)]
        symbol: String,
        /// Order side: buy or sell
        #[arg(long)]
        side: String,
        /// Quantity
        #[arg(long)]
        qty: f64,
        /// Limit price (omit for market orders)
        #[arg(long)]
        price: Option<f64>,
    },
    /// Cancel an open order
    Cancel {
        /// Order ID to cancel
        order_id: String,
    },
    /// List open positions
    Positions,
    /// Query order status or list orders
    Orders {
        /// Order ID to query (omit to list recent orders)
        order_id: Option<String>,
    },
    /// Account summary (balance, margin, P&L)
    Account,
}

// ── Result types ────────────────────────────────────────────────────

#[derive(Serialize)]
struct SubmitResult {
    order_id: String,
    status: String,
    message: String,
}

impl HumanReadable for SubmitResult {
    fn print_human(&self) {
        println!(
            "{} Order submitted: {}",
            "OK".green().bold(),
            self.order_id.cyan()
        );
        println!("  Status:  {}", self.status);
        if !self.message.is_empty() {
            println!("  Message: {}", self.message);
        }
    }
}

#[derive(Serialize)]
struct CancelResult {
    success: bool,
    message: String,
}

impl HumanReadable for CancelResult {
    fn print_human(&self) {
        if self.success {
            println!("{} {}", "OK".green().bold(), self.message);
        } else {
            println!("{} {}", "FAIL".red().bold(), self.message);
        }
    }
}

#[derive(Serialize)]
struct PositionInfo {
    symbol: String,
    quantity: f64,
    avg_price: f64,
    market_value: f64,
    unrealized_pnl: f64,
    realized_pnl: f64,
}

#[derive(Serialize)]
struct PositionsResult {
    positions: Vec<PositionInfo>,
}

impl HumanReadable for PositionsResult {
    fn print_human(&self) {
        if self.positions.is_empty() {
            println!("{}", "No open positions.".dimmed());
            return;
        }
        println!("{}", "Open Positions".bold().underline());
        println!(
            "  {:<12} {:>10} {:>12} {:>14} {:>14}",
            "Symbol", "Qty", "Avg Price", "Mkt Value", "Unreal P&L"
        );
        println!("  {}", "-".repeat(66));
        for p in &self.positions {
            let pnl_colored = if p.unrealized_pnl >= 0.0 {
                format!("{:>14.2}", p.unrealized_pnl).green()
            } else {
                format!("{:>14.2}", p.unrealized_pnl).red()
            };
            println!(
                "  {:<12} {:>10.2} {:>12.2} {:>14.2} {}",
                p.symbol, p.quantity, p.avg_price, p.market_value, pnl_colored
            );
        }
    }
}

#[derive(Serialize)]
struct OrderStatusResult {
    order_id: String,
    symbol: String,
    side: String,
    quantity: f64,
    filled_quantity: f64,
    order_type: String,
    price: Option<f64>,
    status: String,
}

impl HumanReadable for OrderStatusResult {
    fn print_human(&self) {
        println!("{}", "Order Details".bold().underline());
        println!("  Order ID: {}", self.order_id.cyan());
        println!("  Symbol:   {}", self.symbol);
        println!("  Side:     {}", self.side);
        println!("  Type:     {}", self.order_type);
        println!("  Qty:      {} / {} filled", self.filled_quantity, self.quantity);
        if let Some(px) = self.price {
            println!("  Price:    ${:.2}", px);
        }
        println!("  Status:   {}", self.status);
    }
}

#[derive(Serialize)]
struct AccountResult {
    account_id: String,
    cash_balance: f64,
    equity: f64,
    margin_used: f64,
    margin_available: f64,
    buying_power: f64,
    unrealized_pnl: f64,
    realized_pnl: f64,
}

impl HumanReadable for AccountResult {
    fn print_human(&self) {
        println!("{}", "Account Summary".bold().underline());
        println!("  Account:         {}", self.account_id.cyan());
        println!("  Cash Balance:    ${:>14.2}", self.cash_balance);
        println!("  Equity:          ${:>14.2}", self.equity);
        println!("  Margin Used:     ${:>14.2}", self.margin_used);
        println!("  Margin Avail:    ${:>14.2}", self.margin_available);
        println!("  Buying Power:    ${:>14.2}", self.buying_power);
        let upnl = if self.unrealized_pnl >= 0.0 {
            format!("${:>14.2}", self.unrealized_pnl).green().to_string()
        } else {
            format!("${:>14.2}", self.unrealized_pnl).red().to_string()
        };
        let rpnl = if self.realized_pnl >= 0.0 {
            format!("${:>14.2}", self.realized_pnl).green().to_string()
        } else {
            format!("${:>14.2}", self.realized_pnl).red().to_string()
        };
        println!("  Unrealized P&L:  {upnl}");
        println!("  Realized P&L:    {rpnl}");
    }
}

// ── Helpers ─────────────────────────────────────────────────────────

fn parse_side(s: &str) -> Result<i32> {
    match s.to_lowercase().as_str() {
        "buy" | "long" => Ok(trading::OrderSide::Buy.into()),
        "sell" | "short" => Ok(trading::OrderSide::Sell.into()),
        _ => anyhow::bail!("invalid side '{s}': expected 'buy' or 'sell'"),
    }
}

fn side_name(v: i32) -> &'static str {
    match trading::OrderSide::try_from(v) {
        Ok(trading::OrderSide::Buy) => "BUY",
        Ok(trading::OrderSide::Sell) => "SELL",
        _ => "UNKNOWN",
    }
}

fn order_type_name(v: i32) -> &'static str {
    match trading::OrderType::try_from(v) {
        Ok(trading::OrderType::Market) => "MARKET",
        Ok(trading::OrderType::Limit) => "LIMIT",
        Ok(trading::OrderType::Stop) => "STOP",
        Ok(trading::OrderType::StopLimit) => "STOP_LIMIT",
        _ => "UNKNOWN",
    }
}

fn order_status_name(v: i32) -> &'static str {
    match trading::OrderStatus::try_from(v) {
        Ok(trading::OrderStatus::Pending) => "PENDING",
        Ok(trading::OrderStatus::Submitted) => "SUBMITTED",
        Ok(trading::OrderStatus::PartiallyFilled) => "PARTIALLY_FILLED",
        Ok(trading::OrderStatus::Filled) => "FILLED",
        Ok(trading::OrderStatus::Cancelled) => "CANCELLED",
        Ok(trading::OrderStatus::Rejected) => "REJECTED",
        _ => "UNKNOWN",
    }
}

// ── Execute ─────────────────────────────────────────────────────────

impl TradeCommand {
    pub async fn execute(&self, client: &FoxhuntClient, format: OutputFormat) -> Result<()> {
        match &self.action {
            TradeAction::Submit {
                symbol,
                side,
                qty,
                price,
            } => {
                let order_type = if price.is_some() {
                    trading::OrderType::Limit.into()
                } else {
                    trading::OrderType::Market.into()
                };

                let resp = client
                    .trading()
                    .submit_order(trading::SubmitOrderRequest {
                        symbol: symbol.clone(),
                        side: parse_side(side)?,
                        quantity: *qty,
                        order_type,
                        price: *price,
                        stop_price: None,
                        account_id: String::new(),
                        metadata: std::collections::HashMap::new(),
                    })
                    .await?
                    .into_inner();

                let result = SubmitResult {
                    order_id: resp.order_id,
                    status: order_status_name(resp.status).to_owned(),
                    message: resp.message,
                };
                output::render(&result, format)?;
            }

            TradeAction::Cancel { order_id } => {
                let resp = client
                    .trading()
                    .cancel_order(trading::CancelOrderRequest {
                        order_id: order_id.clone(),
                        account_id: String::new(),
                    })
                    .await?
                    .into_inner();

                let result = CancelResult {
                    success: resp.success,
                    message: resp.message,
                };
                output::render(&result, format)?;
            }

            TradeAction::Positions => {
                let resp = client
                    .trading()
                    .get_positions(trading::GetPositionsRequest {
                        account_id: None,
                        symbol: None,
                    })
                    .await?
                    .into_inner();

                let positions = resp
                    .positions
                    .into_iter()
                    .map(|p| PositionInfo {
                        symbol: p.symbol,
                        quantity: p.quantity,
                        avg_price: p.average_price,
                        market_value: p.market_value,
                        unrealized_pnl: p.unrealized_pnl,
                        realized_pnl: p.realized_pnl,
                    })
                    .collect();

                output::render(&PositionsResult { positions }, format)?;
            }

            TradeAction::Orders { order_id } => {
                let Some(oid) = order_id else {
                    println!(
                        "{} Use {} to query a specific order, or {} for streaming.",
                        "INFO".blue().bold(),
                        "fxt trade orders <ORDER_ID>".cyan(),
                        "fxt watch".cyan(),
                    );
                    return Ok(());
                };

                let resp = client
                    .trading()
                    .get_order_status(trading::GetOrderStatusRequest {
                        order_id: oid.clone(),
                    })
                    .await?
                    .into_inner();

                let Some(order) = resp.order else {
                    anyhow::bail!("order {oid} not found");
                };

                let result = OrderStatusResult {
                    order_id: order.order_id,
                    symbol: order.symbol,
                    side: side_name(order.side).to_owned(),
                    quantity: order.quantity,
                    filled_quantity: order.filled_quantity,
                    order_type: order_type_name(order.order_type).to_owned(),
                    price: order.price,
                    status: order_status_name(order.status).to_owned(),
                };
                output::render(&result, format)?;
            }

            TradeAction::Account => {
                let resp = client
                    .broker_gateway()
                    .get_account_state(
                        crate::proto::broker_gateway::GetAccountStateRequest {
                            account_id: String::new(),
                        },
                    )
                    .await?
                    .into_inner();

                let result = AccountResult {
                    account_id: resp.account_id,
                    cash_balance: resp.cash_balance,
                    equity: resp.equity,
                    margin_used: resp.margin_used,
                    margin_available: resp.margin_available,
                    buying_power: resp.buying_power,
                    unrealized_pnl: resp.unrealized_pnl,
                    realized_pnl: resp.realized_pnl,
                };
                output::render(&result, format)?;
            }
        }

        Ok(())
    }
}
