use owo_colors::OwoColorize;
use rust_decimal::Decimal;
use tabled::settings::Style;
use tabled::{Table, Tabled};

use bpx_api_types::capital::Collateral;

use crate::output::OutputFormat;
use crate::risk::{self, LargestExposure, StressTestResult};
use crate::style::Styles;

pub fn print_summary(
    collateral: &Collateral,
    utilization: Option<Decimal>,
    total_exposure: Decimal,
    largest: Option<&LargestExposure>,
    hhi: Decimal,
    format: OutputFormat,
) {
    match format {
        OutputFormat::Json => {
            let largest_json = largest.map(|l| {
                serde_json::json!({
                    "symbol": l.symbol,
                    "notional": l.notional.to_string(),
                    "pct_of_total": l.pct_of_total.to_string(),
                })
            });
            super::print_json(&serde_json::json!({
                "margin_utilization": utilization.map(|u| u.round_dp(1).to_string()),
                "margin_fraction": collateral.margin_fraction.map(|mf| mf.to_string()),
                "net_equity": collateral.net_equity.to_string(),
                "total_exposure": total_exposure.to_string(),
                "largest_position": largest_json,
                "concentration_hhi": hhi.round_dp(0).to_string(),
                "concentration_label": risk::hhi_label(hhi),
            }));
        }
        OutputFormat::Table => {
            let s = Styles::new();
            println!();
            println!("  {}", "RISK OVERVIEW".style(s.cyan));
            println!();

            // Margin utilization with bar
            let util_str = match utilization {
                Some(u) => {
                    let u_rounded = u.round_dp(1);
                    let bar = progress_bar(u_rounded);
                    let color_val = if u_rounded > Decimal::from(80) {
                        format!("{}%", u_rounded).style(s.red).to_string()
                    } else if u_rounded > Decimal::from(50) {
                        format!("{}%", u_rounded).style(s.yellow).to_string()
                    } else {
                        format!("{}%", u_rounded).to_string()
                    };
                    format!("{:<8} {}", color_val, bar)
                }
                None => "N/A (no positions)".to_string(),
            };
            println!("  {:<20} {}", "Margin Used".style(s.bold), util_str);

            // Total exposure
            println!(
                "  {:<20} ${}",
                "Total Exposure".style(s.bold),
                format_usd(total_exposure)
            );

            // Largest position
            match largest {
                Some(l) => {
                    println!(
                        "  {:<20} {} (${}, {}%)",
                        "Largest Position".style(s.bold),
                        l.symbol,
                        format_usd(l.notional),
                        l.pct_of_total.round_dp(1),
                    );
                }
                None => {
                    println!("  {:<20} None", "Largest Position".style(s.bold));
                }
            }

            // Concentration
            let hhi_rounded = hhi.round_dp(0);
            let label = risk::hhi_label(hhi);
            println!(
                "  {:<20} HHI: {} ({})",
                "Concentration".style(s.bold),
                format_number(hhi_rounded),
                label,
            );

            println!();
        }
    }
}

#[derive(Tabled)]
struct StressRow {
    #[tabled(rename = "SCENARIO")]
    scenario: String,
    #[tabled(rename = "PnL")]
    pnl: String,
    #[tabled(rename = "MARGIN")]
    margin: String,
    #[tabled(rename = "STATUS")]
    status: String,
}

pub fn print_stress_tests(results: &[StressTestResult], format: OutputFormat) {
    match format {
        OutputFormat::Json => {
            let items: Vec<serde_json::Value> = results
                .iter()
                .map(|r| {
                    serde_json::json!({
                        "scenario": r.scenario_name,
                        "projected_pnl": r.projected_pnl.round_dp(2).to_string(),
                        "projected_margin_pct": r.projected_margin_pct.round_dp(1).to_string(),
                        "liquidation": r.liquidation,
                    })
                })
                .collect();
            super::print_json(&serde_json::json!({"stress_tests": items}));
        }
        OutputFormat::Table => {
            let s = Styles::new();
            println!();
            println!("  {}", "STRESS TESTS".style(s.cyan));
            println!();

            let rows: Vec<StressRow> = results
                .iter()
                .map(|r| {
                    let pnl_str = if r.projected_pnl < Decimal::ZERO {
                        format!("-${}", format_usd(r.projected_pnl.abs()))
                    } else {
                        format!("+${}", format_usd(r.projected_pnl))
                    };

                    let status = if r.liquidation {
                        "LIQUIDATION".to_string()
                    } else {
                        "OK".to_string()
                    };

                    StressRow {
                        scenario: r.scenario_name.clone(),
                        pnl: pnl_str,
                        margin: format!("{}%", r.projected_margin_pct.round_dp(1)),
                        status,
                    }
                })
                .collect();

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

/// Generate a simple ASCII progress bar (16 chars wide).
fn progress_bar(pct: Decimal) -> String {
    let width = 16;
    let filled = (pct / Decimal::from(100) * Decimal::from(width))
        .to_string()
        .parse::<f64>()
        .unwrap_or(0.0) as usize;
    let filled = filled.min(width);
    let empty = width - filled;
    let bar: String = std::iter::repeat_n('\u{2588}', filled)
        .chain(std::iter::repeat_n('\u{2591}', empty))
        .collect();
    bar
}

/// Format a number with comma separators (integer part only).
fn format_number(d: Decimal) -> String {
    let s = d.to_string();
    let parts: Vec<&str> = s.splitn(2, '.').collect();
    let int_part = parts[0];

    let negative = int_part.starts_with('-');
    let digits = if negative { &int_part[1..] } else { int_part };

    let mut result = String::new();
    for (i, ch) in digits.chars().rev().enumerate() {
        if i > 0 && i % 3 == 0 {
            result.push(',');
        }
        result.push(ch);
    }
    let formatted: String = result.chars().rev().collect();

    if negative {
        format!("-{formatted}")
    } else {
        formatted
    }
}

/// Format a decimal as USD with commas (e.g. 45231.50 -> "45,231").
fn format_usd(d: Decimal) -> String {
    format_number(d.round_dp(0))
}
