use std::io::IsTerminal;
use std::str::FromStr;

use anyhow::Result;
use clap::Subcommand;
use rust_decimal::Decimal;

use bpx_api_types::order::{ExecuteOrderPayload, OrderType, Side, TimeInForce};

use crate::client;
use crate::config::ResolvedConfig;
use crate::output::positions as out;
use crate::output::OutputFormat;

#[derive(Debug, Subcommand)]
pub enum PositionsCommand {
    /// List open futures positions
    List,

    /// Close a futures position (market order, reduce-only)
    Close {
        /// Market symbol (e.g. SOL_USDC_PERP)
        symbol: String,
        /// Quantity to close (defaults to full position)
        #[arg(long)]
        quantity: Option<String>,
    },
}

pub async fn run(
    cmd: &PositionsCommand,
    config: &ResolvedConfig,
    secret: Option<&str>,
    base_url: Option<&str>,
    format: OutputFormat,
) -> Result<()> {
    let c = client::authenticated_client(config, secret, base_url)?;

    match cmd {
        PositionsCommand::List => {
            let sp = client::spinner_for("Fetching positions...", format);
            let result = c.get_open_future_positions().await;
            sp.finish_and_clear();
            let positions = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_positions(&positions, format);
        }

        PositionsCommand::Close { symbol, quantity } => {
            let symbol = symbol.to_uppercase();

            // Fetch current position
            let sp = client::spinner_for("Fetching position...", format);
            let result = c.get_open_future_positions().await;
            sp.finish_and_clear();
            let positions = result.map_err(|e| anyhow::anyhow!("{e}"))?;

            let position = positions
                .iter()
                .find(|p| p.symbol == symbol)
                .ok_or_else(|| anyhow::anyhow!("No open position found for {symbol}"))?;

            let net_qty = Decimal::from_str(&position.net_quantity)
                .map_err(|_| anyhow::anyhow!("Invalid position quantity"))?;

            if net_qty == Decimal::ZERO {
                anyhow::bail!("Position on {symbol} has zero quantity");
            }

            // Determine close side and quantity
            let close_side = if net_qty > Decimal::ZERO {
                Side::Ask // LONG → sell to close
            } else {
                Side::Bid // SHORT → buy to close
            };

            let close_qty = if let Some(qty_str) = quantity {
                Decimal::from_str(qty_str)
                    .map_err(|_| anyhow::anyhow!("Invalid quantity: {qty_str}"))?
            } else {
                net_qty.abs()
            };

            let side_label = if close_side == Side::Ask {
                "SELL"
            } else {
                "BUY"
            };

            // Confirmation in table mode
            if format == OutputFormat::Table && std::io::stdin().is_terminal() {
                use dialoguer::Confirm;
                let confirmed = Confirm::new()
                    .with_prompt(format!(
                        "Close position: {side_label} {close_qty} {symbol} (market, reduce-only)?"
                    ))
                    .default(false)
                    .interact()?;
                if !confirmed {
                    println!("Cancelled.");
                    return Ok(());
                }
            }

            let payload = ExecuteOrderPayload {
                symbol: symbol.clone(),
                side: close_side,
                order_type: OrderType::Market,
                time_in_force: Some(TimeInForce::GTC),
                quantity: Some(close_qty),
                reduce_only: Some(true),
                ..Default::default()
            };

            let sp = client::spinner_for(&format!("Closing position on {symbol}..."), format);
            let result = c.execute_order(payload).await;
            sp.finish_and_clear();
            result.map_err(|e| anyhow::anyhow!("{e}"))?;

            out::print_close_success(&symbol, &close_qty.to_string(), side_label, format);
        }
    }

    Ok(())
}
