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

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

use bpx_api_types::capital::RequestWithdrawalPayload;
use bpx_api_types::Blockchain;

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

#[derive(Debug, Subcommand)]
pub enum CapitalCommand {
    /// List deposit history
    Deposits {
        /// Maximum number of results
        #[arg(long)]
        limit: Option<i64>,
        /// Offset for pagination
        #[arg(long)]
        offset: Option<i64>,
    },

    /// Get deposit address for a blockchain
    DepositAddress {
        /// Blockchain name (e.g. Solana, Ethereum, Bitcoin)
        chain: String,
    },

    /// List withdrawal history
    Withdrawals {
        /// Maximum number of results
        #[arg(long)]
        limit: Option<i64>,
        /// Offset for pagination
        #[arg(long)]
        offset: Option<i64>,
    },

    /// Request a withdrawal
    Withdraw {
        /// Token symbol (e.g. SOL, USDC)
        symbol: String,
        /// Amount to withdraw
        quantity: String,
        /// Destination address
        address: String,
        /// Blockchain name (e.g. Solana, Ethereum)
        chain: String,
        /// Client-assigned ID
        #[arg(long)]
        client_id: Option<String>,
        /// 2FA token
        #[arg(long)]
        two_factor_token: Option<String>,
        /// Auto lend redeem
        #[arg(long)]
        auto_lend_redeem: bool,
        /// Auto borrow
        #[arg(long)]
        auto_borrow: bool,
    },
}

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

    match cmd {
        CapitalCommand::Deposits { limit, offset } => {
            let sp = client::spinner_for("Fetching deposits...", format);
            let result = c.get_deposits(*limit, *offset).await;
            sp.finish_and_clear();
            let deposits = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_deposits(&deposits, format);
        }

        CapitalCommand::DepositAddress { chain } => {
            let blockchain = chain.parse::<Blockchain>().map_err(|_| {
                anyhow::anyhow!(
                    "Invalid blockchain: '{chain}'. Examples: Solana, Ethereum, Bitcoin, Polygon, Arbitrum, Base, Optimism"
                )
            })?;
            let sp =
                client::spinner_for(&format!("Fetching deposit address for {chain}..."), format);
            let result = c.get_deposit_address(blockchain).await;
            sp.finish_and_clear();
            let addr = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_deposit_address(&addr, chain, format);
        }

        CapitalCommand::Withdrawals { limit, offset } => {
            let sp = client::spinner_for("Fetching withdrawals...", format);
            let result = c.get_withdrawals(*limit, *offset).await;
            sp.finish_and_clear();
            let withdrawals = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_withdrawals(&withdrawals, format);
        }

        CapitalCommand::Withdraw {
            symbol,
            quantity,
            address,
            chain,
            client_id,
            two_factor_token,
            auto_lend_redeem,
            auto_borrow,
        } => {
            let symbol = symbol.to_uppercase();
            let qty = Decimal::from_str(quantity)
                .map_err(|_| anyhow::anyhow!("Invalid quantity: {quantity}"))?;
            let blockchain = chain.parse::<Blockchain>().map_err(|_| {
                anyhow::anyhow!(
                    "Invalid blockchain: '{chain}'. Examples: Solana, Ethereum, Bitcoin, Polygon, Arbitrum, Base, Optimism"
                )
            })?;

            // Confirm withdrawal (skip in JSON mode or non-TTY)
            if format == OutputFormat::Table && std::io::stdin().is_terminal() {
                use dialoguer::Confirm;
                let short_addr = if address.chars().count() > 12 {
                    let prefix: String = address.chars().take(6).collect();
                    let suffix: String = address
                        .chars()
                        .rev()
                        .take(4)
                        .collect::<Vec<_>>()
                        .into_iter()
                        .rev()
                        .collect();
                    format!("{prefix}...{suffix}")
                } else {
                    address.clone()
                };
                println!();
                println!(
                    "  Withdraw {} {} to {} via {}",
                    quantity, &symbol, short_addr, chain
                );
                let confirmed = Confirm::new()
                    .with_prompt("  Proceed with withdrawal?")
                    .default(false)
                    .interact()?;
                if !confirmed {
                    println!("  Cancelled.");
                    return Ok(());
                }
            }

            let payload = RequestWithdrawalPayload {
                address: address.clone(),
                blockchain,
                client_id: client_id.clone(),
                quantity: qty,
                symbol: symbol.clone(),
                two_factor_token: two_factor_token.clone(),
                auto_lend_redeem: if *auto_lend_redeem { Some(true) } else { None },
                auto_borrow: if *auto_borrow { Some(true) } else { None },
            };

            let sp = client::spinner_for(
                &format!("Requesting withdrawal of {quantity} {symbol}..."),
                format,
            );
            let result = c.request_withdrawal(payload).await;
            sp.finish_and_clear();
            let withdrawal = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_withdrawal(&withdrawal, format);
        }
    }

    Ok(())
}
