use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;

use crate::tui::app::{AppState, Panel};
use crate::tui::theme;

pub fn render(f: &mut Frame, area: Rect, state: &AppState) {
    let active = state.active_panel == Panel::Balances;
    let count = state
        .balances
        .values()
        .filter(|v| get_total(v) > 0.0)
        .count();
    let info = if count > 0 {
        format!("({count})")
    } else {
        String::new()
    };
    let block = theme::panel_block_divided_with_info("Balances", &info, active);

    let inner = block.inner(area);
    f.render_widget(block, area);

    let mut lines = Vec::new();

    if !state.authenticated {
        lines.push(Line::from(Span::styled(
            "  Not authenticated",
            theme::dim(),
        )));
        let paragraph = Paragraph::new(lines);
        f.render_widget(paragraph, inner);
        return;
    }

    if state.balances.is_empty() {
        lines.push(Line::from(Span::styled("  No balances", theme::dim())));
        let paragraph = Paragraph::new(lines);
        f.render_widget(paragraph, inner);
        return;
    }

    // Compact header
    lines.push(Line::from(vec![
        Span::styled(format!("{:<6}", "ASSET"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>12}", "AVAILABLE"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>10}", "LOCKED"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>12}", "TOTAL"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>6}", "WGHT"), theme::header()),
    ]));

    let max_rows = (inner.height as usize).saturating_sub(1);
    let scroll = state.scroll_offset(Panel::Balances);

    // Collect balances with total > 0
    let mut bal_entries: Vec<(&String, &serde_json::Value)> = state
        .balances
        .iter()
        .filter(|(_, v)| get_total(v) > 0.0)
        .collect();

    // Also include assets from collateral that have totalQuantity > 0
    // but are missing from balances (e.g. USDC used entirely as margin collateral)
    if let Some(ref coll) = state.collateral {
        if let Some(items) = coll.get("collateral").and_then(|v| v.as_array()) {
            for item in items {
                let symbol = item.get("symbol").and_then(|s| s.as_str()).unwrap_or("");
                let total_qty: f64 = item
                    .get("totalQuantity")
                    .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()))
                    .unwrap_or(0.0);
                if total_qty > 0.0 && !bal_entries.iter().any(|(a, _)| a.as_str() == symbol) {
                    // Asset has collateral value but isn't shown — add it from balances map
                    // (it may exist with 0 balance) or skip if truly absent
                    if let Some(bal) = state.balances.get(symbol) {
                        bal_entries.push((state.balances.get_key_value(symbol).unwrap().0, bal));
                    }
                }
            }
        }
    }

    // Sort by total descending (using collateral totalQuantity as fallback for sort)
    bal_entries.sort_by(|(a_name, a), (b_name, b)| {
        let total_a = effective_total(a, a_name, state);
        let total_b = effective_total(b, b_name, state);
        total_b
            .partial_cmp(&total_a)
            .unwrap_or(std::cmp::Ordering::Equal)
    });

    for (i, (asset, bal)) in bal_entries.iter().skip(scroll).take(max_rows).enumerate() {
        let mut available: f64 = bal
            .get("available")
            .and_then(|x| x.as_str())
            .and_then(|s| s.parse().ok())
            .unwrap_or(0.0);
        let mut locked: f64 = bal
            .get("locked")
            .and_then(|x| x.as_str())
            .and_then(|s| s.parse().ok())
            .unwrap_or(0.0);
        let mut total = available + locked;

        // If balance shows 0 but collateral has data, use collateral values
        if total == 0.0 {
            if let Some(coll_item) = collateral_item(asset, state) {
                available = parse_decimal_field(&coll_item, "availableQuantity");
                total = parse_decimal_field(&coll_item, "totalQuantity");
                locked = total - available;
            }
        }

        // Alternating row background
        let bg = if i % 2 == 0 {
            theme::BG
        } else {
            theme::SURFACE_ALT
        };
        let row_style = ratatui::style::Style::default().bg(bg);

        let weight = real_collateral_weight(asset, state);
        let weight_str = format!("{:.0}%", weight * 100.0);
        let weight_style = if weight >= 0.95 {
            theme::bid().bg(bg)
        } else if weight >= 0.5 {
            theme::warning().bg(bg)
        } else {
            theme::dim().bg(bg)
        };

        lines.push(
            Line::from(vec![
                Span::styled(format!("{:<6}", asset), theme::highlight().bg(bg)),
                Span::styled(" ", row_style),
                Span::styled(format!("{available:>12.4}"), theme::normal().bg(bg)),
                Span::styled(" ", row_style),
                Span::styled(
                    format!("{locked:>10.4}"),
                    if locked > 0.0 {
                        theme::warning().bg(bg)
                    } else {
                        theme::dim().bg(bg)
                    },
                ),
                Span::styled(" ", row_style),
                Span::styled(format!("{total:>12.4}"), theme::normal().bg(bg)),
                Span::styled(" ", row_style),
                Span::styled(format!("{weight_str:>6}"), weight_style),
            ])
            .style(row_style),
        );
    }

    let paragraph = Paragraph::new(lines);
    f.render_widget(paragraph, inner);
}

/// Get collateral weight from the API collateral data, falling back to hardcoded defaults.
fn real_collateral_weight(asset: &str, state: &AppState) -> f64 {
    if let Some(ref coll) = state.collateral {
        if let Some(items) = coll.get("collateral").and_then(|v| v.as_array()) {
            if let Some(item) = items
                .iter()
                .find(|i| i.get("symbol").and_then(|s| s.as_str()) == Some(asset))
            {
                if let Some(w) = item
                    .get("collateralWeight")
                    .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()))
                {
                    return w;
                }
            }
        }
    }
    // Fallback defaults
    match asset {
        "USDC" => 1.0,
        "USDT" | "PYUSD" => 0.95,
        "BTC" => 0.95,
        "SOL" | "ETH" => 0.90,
        _ => 0.80,
    }
}

fn get_total(v: &serde_json::Value) -> f64 {
    let avail: f64 = v
        .get("available")
        .and_then(|x| x.as_str())
        .and_then(|s| s.parse().ok())
        .unwrap_or(0.0);
    let locked: f64 = v
        .get("locked")
        .and_then(|x| x.as_str())
        .and_then(|s| s.parse().ok())
        .unwrap_or(0.0);
    let staked: f64 = v
        .get("staked")
        .and_then(|x| x.as_str())
        .and_then(|s| s.parse().ok())
        .unwrap_or(0.0);
    avail + locked + staked
}

/// Get effective total for sorting — uses collateral totalQuantity as fallback.
fn effective_total(bal: &serde_json::Value, asset: &str, state: &AppState) -> f64 {
    let t = get_total(bal);
    if t > 0.0 {
        return t;
    }
    collateral_item(asset, state)
        .map(|item| parse_decimal_field(&item, "totalQuantity"))
        .unwrap_or(0.0)
}

/// Look up a collateral item by symbol.
fn collateral_item(asset: &str, state: &AppState) -> Option<serde_json::Value> {
    state
        .collateral
        .as_ref()?
        .get("collateral")?
        .as_array()?
        .iter()
        .find(|i| i.get("symbol").and_then(|s| s.as_str()) == Some(asset))
        .cloned()
}

fn parse_decimal_field(val: &serde_json::Value, key: &str) -> f64 {
    val.get(key)
        .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()))
        .unwrap_or(0.0)
}
