use std::collections::HashMap;

use bpx_api_types::account::{
    AccountMaxBorrow, AccountMaxOrder, AccountMaxWithdrawal, AccountSettings,
};
use bpx_api_types::capital::{Balance, Collateral};
use owo_colors::OwoColorize;
use tabled::settings::Style;
use tabled::{Table, Tabled};

use crate::output::OutputFormat;
use crate::style::Styles;

#[derive(Tabled)]
struct BalanceRow {
    #[tabled(rename = "ASSET")]
    symbol: String,
    #[tabled(rename = "AVAILABLE")]
    available: String,
    #[tabled(rename = "LOCKED")]
    locked: String,
    #[tabled(rename = "STAKED")]
    staked: String,
}

pub fn print_balances(balances: &HashMap<String, Balance>, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(balances),
        OutputFormat::Table => {
            if balances.is_empty() {
                println!("No balances found.");
                return;
            }
            let mut rows: Vec<BalanceRow> = balances
                .iter()
                .filter(|(_, b)| b.total() > rust_decimal::Decimal::ZERO)
                .map(|(symbol, b)| BalanceRow {
                    symbol: symbol.clone(),
                    available: b.available.to_string(),
                    locked: b.locked.to_string(),
                    staked: b.staked.to_string(),
                })
                .collect();
            rows.sort_by(|a, b| a.symbol.cmp(&b.symbol));

            if rows.is_empty() {
                println!("No non-zero balances found.");
                return;
            }

            let mut table = Table::new(rows);
            table.with(Style::blank());
            println!("{table}");
        }
    }
}

pub fn print_account(account: &AccountSettings, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(account),
        OutputFormat::Table => {
            let s = Styles::new();
            println!("{}", "Account Settings".style(s.cyan));
            println!();
            println!("  {}", "Trading".style(s.bold));
            println!(
                "    Spot Maker Fee:    {}",
                super::format_pct(&account.spot_maker_fee)
            );
            println!(
                "    Spot Taker Fee:    {}",
                super::format_pct(&account.spot_taker_fee)
            );
            println!(
                "    Perp Maker Fee:    {}",
                super::format_pct(&account.futures_maker_fee)
            );
            println!(
                "    Perp Taker Fee:    {}",
                super::format_pct(&account.futures_taker_fee)
            );
            println!();
            println!("  {}", "Limits".style(s.bold));
            println!("    Leverage Limit:    {}", account.leverage_limit);
            println!("    Borrow Limit:      {}", account.borrow_limit);
            println!("    Limit Orders:      {}", account.limit_orders);
            println!("    Trigger Orders:    {}", account.trigger_orders);
            println!("    Position Limit:    {}", account.position_limit);
            println!();
            println!("  {}", "Automation".style(s.bold));
            println!(
                "    Auto Lend:         {}",
                super::format_bool(account.auto_lend)
            );
            println!(
                "    Auto Repay:        {}",
                super::format_bool(account.auto_repay_borrows)
            );
            println!(
                "    Auto Borrow:       {}",
                super::format_bool(account.auto_borrow_settlements)
            );
            println!(
                "    Auto Realize PnL:  {}",
                super::format_bool(account.auto_realize_pnl)
            );
            println!();
            println!("  {}", "Status".style(s.bold));
            println!(
                "    Liquidating:       {}",
                if account.liquidating {
                    "Yes".style(s.red).to_string()
                } else {
                    "No".to_string()
                }
            );
        }
    }
}

#[derive(Tabled)]
struct CollateralItemRow {
    #[tabled(rename = "SYMBOL")]
    symbol: String,
    #[tabled(rename = "MARK PRICE")]
    mark_price: String,
    #[tabled(rename = "TOTAL QTY")]
    total_qty: String,
    #[tabled(rename = "NOTIONAL")]
    notional: String,
    #[tabled(rename = "WEIGHT")]
    weight: String,
    #[tabled(rename = "VALUE")]
    value: String,
    #[tabled(rename = "AVAILABLE")]
    available: String,
    #[tabled(rename = "LEND QTY")]
    lend_qty: String,
    #[tabled(rename = "OPEN ORDERS")]
    open_orders: String,
}

pub fn print_collateral(collateral: &Collateral, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(collateral),
        OutputFormat::Table => {
            let s = Styles::new();
            println!("{}", "Collateral Summary".style(s.cyan));
            println!("  Net Equity:          {}", collateral.net_equity);
            println!(
                "  Net Equity Available: {}",
                collateral.net_equity_available
            );
            println!("  Net Equity Locked:   {}", collateral.net_equity_locked);
            println!("  Assets Value:        {}", collateral.assets_value);
            println!("  Liabilities Value:   {}", collateral.liabilities_value);
            println!("  Borrow Liability:    {}", collateral.borrow_liability);
            if let Some(mf) = collateral.margin_fraction {
                println!("  Margin Fraction:     {}", mf);
            }
            println!("  IMF:                 {}", collateral.imf);
            println!("  MMF:                 {}", collateral.mmf);
            println!("  Unrealized PnL:      {}", collateral.pnl_unrealized);
            println!("  Unsettled Equity:    {}", collateral.unsettled_equity);
            println!(
                "  Net Exposure Futures: {}",
                collateral.net_exposure_futures
            );
            println!();

            if collateral.collateral.is_empty() {
                println!("No collateral items.");
                return;
            }
            let rows: Vec<CollateralItemRow> = collateral
                .collateral
                .iter()
                .map(|c| CollateralItemRow {
                    symbol: c.symbol.clone(),
                    mark_price: c.asset_mark_price.to_string(),
                    total_qty: c.total_quantity.to_string(),
                    notional: c.balance_notional.to_string(),
                    weight: c.collateral_weight.to_string(),
                    value: c.collateral_value.to_string(),
                    available: c.available_quantity.to_string(),
                    lend_qty: c.lend_quantity.to_string(),
                    open_orders: c.open_order_quantity.to_string(),
                })
                .collect();
            let mut table = Table::new(rows);
            table.with(Style::blank());
            println!("{table}");
        }
    }
}

#[derive(Tabled)]
struct MaxBorrowRow {
    #[tabled(rename = "SYMBOL")]
    symbol: String,
    #[tabled(rename = "MAX BORROW QTY")]
    max_borrow_qty: String,
}

pub fn print_max_borrow(max_borrow: &AccountMaxBorrow, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(max_borrow),
        OutputFormat::Table => {
            let rows = vec![MaxBorrowRow {
                symbol: max_borrow.symbol.clone(),
                max_borrow_qty: max_borrow.max_borrow_quantity.to_string(),
            }];
            let mut table = Table::new(rows);
            table.with(Style::blank());
            println!("{table}");
        }
    }
}

#[derive(Tabled)]
struct MaxOrderRow {
    #[tabled(rename = "SYMBOL")]
    symbol: String,
    #[tabled(rename = "SIDE")]
    side: String,
    #[tabled(rename = "MAX QTY")]
    max_qty: String,
    #[tabled(rename = "PRICE")]
    price: String,
}

pub fn print_max_order(max_order: &AccountMaxOrder, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(max_order),
        OutputFormat::Table => {
            let rows = vec![MaxOrderRow {
                symbol: max_order.symbol.clone(),
                side: super::display_side(&max_order.side).to_string(),
                max_qty: max_order.max_order_quantity.to_string(),
                price: max_order
                    .price
                    .map(|p| p.to_string())
                    .unwrap_or_else(|| "-".to_string()),
            }];
            let mut table = Table::new(rows);
            table.with(Style::blank());
            println!("{table}");
        }
    }
}

#[derive(Tabled)]
struct MaxWithdrawalRow {
    #[tabled(rename = "SYMBOL")]
    symbol: String,
    #[tabled(rename = "MAX WITHDRAWAL QTY")]
    max_withdrawal_qty: String,
}

pub fn print_max_withdrawal(max_withdrawal: &AccountMaxWithdrawal, format: OutputFormat) {
    match format {
        OutputFormat::Json => super::print_json(max_withdrawal),
        OutputFormat::Table => {
            let rows = vec![MaxWithdrawalRow {
                symbol: max_withdrawal.symbol.clone(),
                max_withdrawal_qty: max_withdrawal.max_withdrawal_quantity.to_string(),
            }];
            let mut table = Table::new(rows);
            table.with(Style::blank());
            println!("{table}");
        }
    }
}

pub fn print_update_success(format: OutputFormat) {
    match format {
        OutputFormat::Json => {
            super::print_json(&serde_json::json!({"status": "updated"}));
        }
        OutputFormat::Table => {
            let s = Styles::new();
            println!("{} Account updated", "\u{2713}".style(s.green));
        }
    }
}

pub fn print_dust_success(format: OutputFormat) {
    match format {
        OutputFormat::Json => {
            super::print_json(&serde_json::json!({"status": "dust_converted"}));
        }
        OutputFormat::Table => {
            let s = Styles::new();
            println!("{} Dust balance converted", "\u{2713}".style(s.green));
        }
    }
}
