use owo_colors::OwoColorize;
use rust_decimal::Decimal;

use crate::commands::scenario::ScenarioResult;
use crate::output::OutputFormat;
use crate::style::Styles;

pub fn print_scenario(result: &ScenarioResult, format: OutputFormat) {
    match format {
        OutputFormat::Json => print_json(result),
        OutputFormat::Table => print_table(result),
    }
}

fn print_json(result: &ScenarioResult) {
    let delta_mf = match (
        result.projected.margin_fraction,
        result.current.margin_fraction,
    ) {
        (Some(p), Some(c)) => Some(p - c),
        _ => None,
    };
    let delta_liq = match (
        result.projected.liquidation_distance_pct,
        result.current.liquidation_distance_pct,
    ) {
        (Some(p), Some(c)) => Some(p - c),
        _ => None,
    };

    super::print_json(&serde_json::json!({
        "scenario": result.label,
        "current": {
            "margin_fraction": result.current.margin_fraction,
            "net_exposure": result.current.net_exposure,
            "unrealized_pnl": result.current.unrealized_pnl,
            "net_equity": result.current.net_equity,
            "liquidation_distance_pct": result.current.liquidation_distance_pct,
        },
        "projected": {
            "margin_fraction": result.projected.margin_fraction,
            "net_exposure": result.projected.net_exposure,
            "unrealized_pnl": result.projected.unrealized_pnl,
            "net_equity": result.projected.net_equity,
            "liquidation_distance_pct": result.projected.liquidation_distance_pct,
        },
        "delta": {
            "margin_fraction": delta_mf,
            "net_exposure": result.projected.net_exposure - result.current.net_exposure,
            "unrealized_pnl": result.projected.unrealized_pnl - result.current.unrealized_pnl,
            "net_equity": result.projected.net_equity - result.current.net_equity,
            "liquidation_distance_pct": delta_liq,
        },
    }));
}

fn print_table(result: &ScenarioResult) {
    let s = Styles::new();

    println!();
    println!(
        "  {} {}",
        "SCENARIO:".style(s.cyan),
        result.label.style(s.bold)
    );
    println!();
    println!(
        "  {:<22}{:>14}{:>14}{:>14}",
        "METRIC".style(s.dim),
        "CURRENT".style(s.dim),
        "PROJECTED".style(s.dim),
        "CHANGE".style(s.dim),
    );
    println!();

    // Margin Fraction
    print_metric_row(
        "Margin Fraction",
        result.current.margin_fraction.map(|v| format_decimal(v, 4)),
        result
            .projected
            .margin_fraction
            .map(|v| format_decimal(v, 4)),
        &s,
        true, // higher is better
    );

    // Liquidation Distance
    print_metric_row(
        "Liquidation Dist",
        result
            .current
            .liquidation_distance_pct
            .map(|v| format!("{}%", format_decimal(v, 1))),
        result
            .projected
            .liquidation_distance_pct
            .map(|v| format!("{}%", format_decimal(v, 1))),
        &s,
        true, // higher is better
    );

    // Unrealized PnL
    print_metric_row(
        "Unrealized PnL",
        Some(format_usd(result.current.unrealized_pnl)),
        Some(format_usd(result.projected.unrealized_pnl)),
        &s,
        true, // higher is better
    );

    // Net Equity
    print_metric_row(
        "Net Equity",
        Some(format_usd(result.current.net_equity)),
        Some(format_usd(result.projected.net_equity)),
        &s,
        true, // higher is better
    );

    // Net Exposure
    print_metric_row(
        "Net Exposure",
        Some(format_usd(result.current.net_exposure)),
        Some(format_usd(result.projected.net_exposure)),
        &s,
        false, // lower is typically safer
    );

    println!();
}

fn print_metric_row(
    label: &str,
    current: Option<String>,
    projected: Option<String>,
    s: &Styles,
    higher_is_better: bool,
) {
    let cur_str = current.as_deref().unwrap_or("-");
    let proj_str = projected.as_deref().unwrap_or("-");

    // Compute change string
    let change_str = match (&current, &projected) {
        (Some(c), Some(p)) if c == p => "  -".to_string(),
        _ => {
            // Try to parse numeric values for delta
            let cur_num = current.as_deref().and_then(parse_numeric);
            let proj_num = projected.as_deref().and_then(parse_numeric);
            match (cur_num, proj_num) {
                (Some(c), Some(p)) => {
                    let delta = p - c;
                    let sign = if delta > Decimal::ZERO { "+" } else { "" };
                    // For USD values
                    if current.as_deref().is_some_and(|s| s.contains('$')) {
                        format!("{sign}{}", format_usd(delta))
                    } else if current.as_deref().is_some_and(|s| s.contains('%')) {
                        format!("{sign}{}%", format_decimal(delta, 1))
                    } else {
                        format!("{sign}{}", format_decimal(delta, 4))
                    }
                }
                _ => "-".to_string(),
            }
        }
    };

    // Color the change
    let change_colored = {
        let delta = parse_numeric(&change_str);
        match delta {
            Some(d) if d > Decimal::ZERO => {
                if higher_is_better {
                    format!("{}", change_str.style(s.green))
                } else {
                    format!("{}", change_str.style(s.red))
                }
            }
            Some(d) if d < Decimal::ZERO => {
                if higher_is_better {
                    format!("{}", change_str.style(s.red))
                } else {
                    format!("{}", change_str.style(s.green))
                }
            }
            _ => change_str,
        }
    };

    println!(
        "  {:<22}{:>14}{:>14}{:>14}",
        label, cur_str, proj_str, change_colored,
    );
}

fn format_decimal(d: Decimal, dp: u32) -> String {
    d.round_dp(dp).normalize().to_string()
}

fn format_usd(d: Decimal) -> String {
    let rounded = d.round_dp(2);
    if rounded < Decimal::ZERO {
        format!("-${}", (-rounded).normalize())
    } else {
        format!("${}", rounded.normalize())
    }
}

/// Parse a numeric value from a display string, stripping $, %, +, commas.
fn parse_numeric(s: &str) -> Option<Decimal> {
    let cleaned: String = s
        .chars()
        .filter(|c| *c != '$' && *c != '%' && *c != ',' && *c != ' ')
        .collect();
    Decimal::from_str_exact(&cleaned).ok()
}
