use std::collections::VecDeque;

use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Paragraph, Sparkline};
use ratatui::Frame;

use crate::tui::app::AppState;
use crate::tui::event::LatencySample;
use crate::tui::theme;

pub fn render(f: &mut Frame, area: Rect, state: &AppState) {
    let show_latency =
        state.show_latency && (!state.latency_history.is_empty() || state.ping_rtt.is_some());

    if !show_latency {
        let block = theme::panel_block_divided("Account", false);
        let inner = block.inner(area);
        f.render_widget(block, area);
        render_account_summary(f, inner, state);
        return;
    }

    let block = theme::panel_block_divided("Latency", false);
    let inner = block.inner(area);
    f.render_widget(block, area);

    // Split into text stats area and sparklines area
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(4), // stats text
            Constraint::Min(1),    // sparklines
        ])
        .split(inner);

    // Latency stats
    let mut lines: Vec<Line> = Vec::new();

    if !state.latency_history.is_empty() {
        let (net_min, net_avg, net_max) = latency_stats(&state.latency_history, |s| s.network_us);
        let (eng_min, eng_avg, eng_max) = latency_stats(&state.latency_history, |s| s.engine_us);

        let net_style = if net_avg < 0.01 {
            // Near-zero likely means clock skew (client ahead of server)
            theme::dim()
        } else {
            theme::normal()
        };

        lines.push(Line::from(vec![
            Span::styled(" Net  ", theme::dim()),
            Span::styled(format_latency(net_avg), net_style),
            Span::styled(" avg  ", theme::dim()),
            Span::styled(format_latency(net_min), theme::dim()),
            Span::styled("-", theme::dim()),
            Span::styled(format_latency(net_max), theme::dim()),
        ]));
        lines.push(Line::from(vec![
            Span::styled(" Eng  ", theme::dim()),
            Span::styled(format_latency(eng_avg), theme::normal()),
            Span::styled(" avg  ", theme::dim()),
            Span::styled(format_latency(eng_min), theme::dim()),
            Span::styled("-", theme::dim()),
            Span::styled(format_latency(eng_max), theme::dim()),
        ]));
    }

    if let Some(ref rtt) = state.ping_rtt {
        let rtt_ms = rtt.as_secs_f64() * 1000.0;
        let rtt_style = if rtt_ms < 50.0 {
            theme::bid()
        } else if rtt_ms < 200.0 {
            theme::warning()
        } else {
            theme::ask()
        };
        lines.push(Line::from(vec![
            Span::styled(" RTT  ", theme::dim()),
            Span::styled(format_latency(rtt_ms), rtt_style),
        ]));
    }

    if lines.is_empty() {
        lines.push(Line::from(Span::styled(
            " Waiting for data...",
            theme::dim(),
        )));
    }

    let stats = Paragraph::new(lines);
    f.render_widget(stats, chunks[0]);

    // Sparklines
    if !state.latency_history.is_empty() && chunks[1].height >= 2 {
        let sparkline_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
            .split(chunks[1]);

        // Network latency sparkline (raw microseconds for full resolution)
        let net_data: Vec<u64> = state
            .latency_history
            .iter()
            .map(|s| s.network_us.max(0) as u64)
            .collect();
        let net_sparkline = Sparkline::default()
            .data(&net_data)
            .style(ratatui::style::Style::default().fg(theme::ACCENT));
        f.render_widget(net_sparkline, sparkline_chunks[0]);

        // Engine delay sparkline (raw microseconds)
        let eng_data: Vec<u64> = state
            .latency_history
            .iter()
            .map(|s| s.engine_us.max(0) as u64)
            .collect();
        let eng_sparkline = Sparkline::default()
            .data(&eng_data)
            .style(ratatui::style::Style::default().fg(theme::YELLOW));
        f.render_widget(eng_sparkline, sparkline_chunks[1]);
    }
}

/// Render account summary + margin health on the left, borrow/lend on the right.
fn render_account_summary(f: &mut Frame, area: Rect, state: &AppState) {
    // Collateral breakdown mode (Shift+C toggle)
    if state.show_collateral {
        render_collateral_breakdown(f, area, state);
        return;
    }

    let has_bl = !state.borrow_lend_markets.is_empty() || !state.borrow_lend_positions.is_empty();

    if has_bl && area.width >= 40 {
        // Split into two columns: account (left) | borrow/lend (right)
        let cols = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
            .split(area);

        render_account_left(f, cols[0], state);
        render_borrow_lend_right(f, cols[1], state);
    } else {
        render_account_left(f, area, state);
    }
}

/// Left column: account summary (equity, margin, PnL).
fn render_account_left(f: &mut Frame, area: Rect, state: &AppState) {
    let mut lines: Vec<Line> = Vec::new();

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

    if let Some(ref coll) = state.collateral {
        let net_equity = coll_f64(coll, "netEquity");
        let net_avail = coll_f64(coll, "netEquityAvailable");
        let net_locked = coll_f64(coll, "netEquityLocked");
        let upnl = coll_f64(coll, "pnlUnrealized");
        let assets_value = coll_f64(coll, "assetsValue");
        let imf = coll_f64(coll, "imf");
        let mmf = coll_f64(coll, "mmf");
        let margin_fraction = coll.get("marginFraction").and_then(|v| {
            v.as_str()
                .and_then(|s| s.parse::<f64>().ok())
                .or(v.as_f64())
        });

        let has_positions = !state.positions.is_empty();

        // Margin health — show as first line with visual bar when positions are open
        if let Some(mf) = margin_fraction {
            if has_positions {
                let mf_pct = mf * 100.0;
                let mf_style = if mmf > 0.0 && mf < mmf * 1.5 {
                    ratatui::style::Style::default()
                        .fg(theme::RED)
                        .add_modifier(ratatui::style::Modifier::BOLD)
                } else if mmf > 0.0 && mf < mmf * 3.0 {
                    theme::warning()
                } else {
                    theme::bid()
                };

                // Visual bar: 10 chars wide, filled proportional to margin usage
                // Higher margin fraction = safer, so fill proportionally (cap at 100%)
                let bar_width = 10usize;
                let fill_ratio = (mf_pct / 100.0).clamp(0.0, 1.0);
                let filled = (fill_ratio * bar_width as f64).round() as usize;
                let empty = bar_width.saturating_sub(filled);
                let bar: String = "\u{2588}".repeat(filled) + &"\u{2591}".repeat(empty);

                lines.push(Line::from(vec![
                    Span::styled(" Margin ", theme::dim()),
                    Span::styled(bar, mf_style),
                    Span::styled(format!(" {mf_pct:.1}%"), mf_style),
                ]));

                // Margin utilization: mmf / margin_fraction * 100
                // Higher = closer to liquidation
                if mmf > 0.0 {
                    let util_pct = (mmf / mf) * 100.0;
                    let util_style = if util_pct > 80.0 {
                        ratatui::style::Style::default()
                            .fg(theme::RED)
                            .add_modifier(ratatui::style::Modifier::BOLD)
                    } else if util_pct > 50.0 {
                        theme::warning()
                    } else {
                        theme::bid()
                    };
                    lines.push(detail_line(
                        "Margin Used",
                        format!("{util_pct:.1}%"),
                        util_style,
                    ));
                }

                // Liquidation distance for current symbol's position
                if let Some(pos) = state
                    .positions
                    .iter()
                    .find(|p| p.get("symbol").and_then(|v| v.as_str()) == Some(&state.symbol))
                {
                    let liq_price: Option<f64> = pos
                        .get("estLiquidationPrice")
                        .or_else(|| pos.get("est_liquidation_price"))
                        .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()));
                    let mark: Option<f64> = state.mark_price.as_ref().and_then(|mp| {
                        mp.get("markPrice")
                            .or_else(|| mp.get("mark_price"))
                            .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()))
                    });
                    if let (Some(liq), Some(mk)) = (liq_price, mark) {
                        if mk > 0.0 && liq > 0.0 {
                            let dist_pct = ((liq - mk) / mk) * 100.0;
                            let dist_style = if dist_pct.abs() < 10.0 {
                                ratatui::style::Style::default()
                                    .fg(theme::RED)
                                    .add_modifier(ratatui::style::Modifier::BOLD)
                            } else if dist_pct.abs() < 25.0 {
                                theme::warning()
                            } else {
                                theme::dim()
                            };
                            lines.push(detail_line(
                                "Liq dist",
                                format!("{dist_pct:+.1}%"),
                                dist_style,
                            ));
                        }
                    }
                }
            }

            // Concentration (HHI) — shown when positions are open
            if let Some(hhi) = state.risk_hhi {
                let (label, style) = if hhi < 2500.0 {
                    ("Diversified", theme::bid())
                } else if hhi < 5000.0 {
                    ("Moderate", theme::warning())
                } else {
                    (
                        "Concentrated",
                        ratatui::style::Style::default()
                            .fg(theme::RED)
                            .add_modifier(ratatui::style::Modifier::BOLD),
                    )
                };
                lines.push(detail_line("Concentration", label.to_string(), style));
            }

            // Largest position
            if let Some((ref sym, pct)) = state.risk_largest_pct {
                let display_sym = sym.replace("_USDC_PERP", "_PP").replace("_USDC", "");
                let style = if pct > 50.0 {
                    ratatui::style::Style::default()
                        .fg(theme::RED)
                        .add_modifier(ratatui::style::Modifier::BOLD)
                } else {
                    theme::secondary()
                };
                lines.push(detail_line(
                    "Largest",
                    format!("{display_sym} {pct:.0}%"),
                    style,
                ));
            }
        }

        if net_equity != 0.0 {
            lines.push(detail_line(
                "Net Equity",
                format!("${:.2}", net_equity),
                theme::normal(),
            ));
        }
        if assets_value != 0.0 {
            lines.push(detail_line(
                "Assets Value",
                format!("${:.2}", assets_value),
                theme::normal(),
            ));
        }
        if upnl != 0.0 {
            let pnl_style = if upnl >= 0.0 {
                theme::bid()
            } else {
                theme::ask()
            };
            lines.push(detail_line("uPnL", format!("{:+.2}", upnl), pnl_style));
        }
        if net_avail != 0.0 || net_locked != 0.0 {
            lines.push(detail_line(
                "Available",
                format!("${:.2}", net_avail),
                theme::secondary(),
            ));
            lines.push(detail_line(
                "Locked",
                format!("${:.2}", net_locked),
                if net_locked > 0.0 {
                    theme::warning()
                } else {
                    theme::dim()
                },
            ));
        }

        // Margin health (non-bar version — shown when no positions)
        if let Some(mf) = margin_fraction {
            if !has_positions {
                let mf_pct = mf * 100.0;
                let mf_style = if mmf > 0.0 && mf < mmf * 1.5 {
                    ratatui::style::Style::default()
                        .fg(theme::RED)
                        .add_modifier(ratatui::style::Modifier::BOLD)
                } else if mmf > 0.0 && mf < mmf * 3.0 {
                    theme::warning()
                } else {
                    theme::bid()
                };
                lines.push(detail_line(
                    "Margin Frac",
                    format!("{mf_pct:.1}%"),
                    mf_style,
                ));
            }
        }
        if imf > 0.0 {
            lines.push(detail_line(
                "IMF",
                format!("{:.1}%", imf * 100.0),
                theme::dim(),
            ));
        }
        if mmf > 0.0 {
            lines.push(detail_line(
                "MMF",
                format!("{:.1}%", mmf * 100.0),
                theme::dim(),
            ));
        }
    } else {
        lines.push(Line::from(Span::styled(
            " Waiting for data...",
            theme::dim(),
        )));
    }

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

/// Right column: borrow/lend positions, rates, and earnings.
fn render_borrow_lend_right(f: &mut Frame, area: Rect, state: &AppState) {
    let mut lines: Vec<Line> = Vec::new();

    lines.push(Line::from(Span::styled(
        " Borrow / Lend",
        theme::highlight(),
    )));

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

    // Show active positions with rates
    let mut total_earn_per_hr: f64 = 0.0;
    let mut total_pay_per_hr: f64 = 0.0;

    for pos in &state.borrow_lend_positions {
        let symbol = pos.get("symbol").and_then(|s| s.as_str()).unwrap_or("?");
        let net_qty =
            val_f64(Some(pos), "netQuantity").or_else(|| val_f64(Some(pos), "net_quantity"));
        let mark_price = val_f64(Some(pos), "markPrice")
            .or_else(|| val_f64(Some(pos), "mark_price"))
            .unwrap_or(1.0);

        let qty = match net_qty {
            Some(q) if q.abs() > 0.0001 => q,
            _ => continue,
        };

        let is_lending = qty > 0.0;
        let notional = qty.abs() * mark_price;

        // Get rate from borrow/lend markets
        let rate = state.borrow_lend_markets.get(symbol).and_then(|m| {
            if is_lending {
                val_f64(Some(m), "lendInterestRate")
                    .or_else(|| val_f64(Some(m), "lend_interest_rate"))
            } else {
                val_f64(Some(m), "borrowInterestRate")
                    .or_else(|| val_f64(Some(m), "borrow_interest_rate"))
            }
        });

        // Rates are annualized (APR). Convert to hourly: notional * rate / 8760.
        let hourly = rate.map(|r| notional * r / 8760.0).unwrap_or(0.0);

        if is_lending {
            total_earn_per_hr += hourly;
        } else {
            total_pay_per_hr += hourly;
        }

        let label = if is_lending { "Lend" } else { "Borw" };
        let qty_str = format_compact(qty.abs());
        let style = if is_lending {
            theme::bid()
        } else {
            theme::ask()
        };

        let rate_str = rate
            .map(|r| format!("{:.2}% APR", r * 100.0))
            .unwrap_or_else(|| "-".to_string());

        lines.push(Line::from(vec![
            Span::styled(format!(" {label} "), style),
            Span::styled(format!("{qty_str} {symbol}"), theme::secondary()),
            Span::styled(format!(" {rate_str}"), theme::dim()),
        ]));
    }

    // Show top market rates if no positions (or as supplementary info)
    if state.borrow_lend_positions.is_empty() && !state.borrow_lend_markets.is_empty() {
        let max_rows = (area.height as usize).saturating_sub(3);
        let mut entries: Vec<(&str, f64, f64)> = state
            .borrow_lend_markets
            .iter()
            .filter_map(|(sym, m)| {
                let lend = val_f64(Some(m), "lendInterestRate")
                    .or_else(|| val_f64(Some(m), "lend_interest_rate"))
                    .unwrap_or(0.0);
                let borrow = val_f64(Some(m), "borrowInterestRate")
                    .or_else(|| val_f64(Some(m), "borrow_interest_rate"))
                    .unwrap_or(0.0);
                if lend > 0.0 || borrow > 0.0 {
                    Some((sym.as_str(), lend, borrow))
                } else {
                    None
                }
            })
            .collect();
        entries.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

        lines.push(Line::from(vec![
            Span::styled(" ", theme::dim()),
            Span::styled(format!("{:<6}", "ASSET"), theme::dim()),
            Span::styled(format!(" {:>7}", "LEND"), theme::dim()),
            Span::styled(format!(" {:>7}", "BORW"), theme::dim()),
        ]));

        for (sym, lend, borrow) in entries.iter().take(max_rows) {
            lines.push(Line::from(vec![
                Span::styled(" ", theme::dim()),
                Span::styled(format!("{:<6}", sym), theme::secondary()),
                Span::styled(format!(" {:>6.3}%", lend * 100.0), theme::bid()),
                Span::styled(format!(" {:>6.3}%", borrow * 100.0), theme::ask()),
            ]));
        }
    }

    // Earnings summary
    if total_earn_per_hr > 0.0 || total_pay_per_hr > 0.0 {
        lines.push(Line::from("")); // spacer
        let net_per_hr = total_earn_per_hr - total_pay_per_hr;
        let net_style = if net_per_hr >= 0.0 {
            theme::bid()
        } else {
            theme::ask()
        };

        if total_earn_per_hr > 0.0 {
            lines.push(Line::from(vec![
                Span::styled(" Earning  ", theme::dim()),
                Span::styled(format!("${:.4}/hr", total_earn_per_hr), theme::bid()),
            ]));
        }
        if total_pay_per_hr > 0.0 {
            lines.push(Line::from(vec![
                Span::styled(" Paying   ", theme::dim()),
                Span::styled(format!("${:.4}/hr", total_pay_per_hr), theme::ask()),
            ]));
        }
        if total_earn_per_hr > 0.0 && total_pay_per_hr > 0.0 {
            lines.push(Line::from(vec![
                Span::styled(" Net      ", theme::dim()),
                Span::styled(format!("${:+.4}/hr", net_per_hr), net_style),
            ]));
        }

        // Daily projection
        let net_per_day = net_per_hr * 24.0;
        lines.push(Line::from(vec![
            Span::styled("          ", theme::dim()),
            Span::styled(format!("${:+.2}/day", net_per_day), net_style),
        ]));
    }

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

/// Collateral breakdown: per-asset list with symbol, qty, weight, value.
fn render_collateral_breakdown(f: &mut Frame, area: Rect, state: &AppState) {
    let mut lines: Vec<Line> = Vec::new();

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

    let items = state
        .collateral
        .as_ref()
        .and_then(|c| c.get("collateral"))
        .and_then(|v| v.as_array());

    match items {
        Some(arr) if !arr.is_empty() => {
            // Header
            lines.push(Line::from(vec![
                Span::styled(format!(" {:<6}", "ASSET"), theme::dim()),
                Span::styled(format!(" {:>10}", "QTY"), theme::dim()),
                Span::styled(format!(" {:>6}", "WGHT"), theme::dim()),
                Span::styled(format!(" {:>10}", "VALUE"), theme::dim()),
            ]));

            let mut total_value = 0.0_f64;
            for item in arr {
                let sym = item.get("symbol").and_then(|v| v.as_str()).unwrap_or("?");
                let qty = val_f64(Some(item), "totalQuantity").unwrap_or(0.0);
                let weight = val_f64(Some(item), "collateralWeight").unwrap_or(0.0);
                let value = val_f64(Some(item), "collateralValue").unwrap_or(0.0);

                if qty.abs() < 0.0001 && value.abs() < 0.01 {
                    continue;
                }

                total_value += value;

                let weight_style = if weight >= 1.0 {
                    theme::bid()
                } else if weight >= 0.5 {
                    theme::secondary()
                } else {
                    theme::warning()
                };

                lines.push(Line::from(vec![
                    Span::styled(format!(" {:<6}", sym), theme::normal()),
                    Span::styled(format!(" {:>10}", format_compact(qty)), theme::secondary()),
                    Span::styled(format!(" {:>5.0}%", weight * 100.0), weight_style),
                    Span::styled(format!(" {:>10}", format_usd(value)), theme::normal()),
                ]));
            }

            // Total
            if total_value > 0.0 {
                lines.push(Line::from(vec![
                    Span::styled(format!(" {:<6}", "TOTAL"), theme::dim()),
                    Span::styled(format!(" {:>10}", ""), theme::dim()),
                    Span::styled(format!(" {:>6}", ""), theme::dim()),
                    Span::styled(
                        format!(" {:>10}", format_usd(total_value)),
                        theme::highlight(),
                    ),
                ]));
            }
        }
        _ => {
            lines.push(Line::from(Span::styled(
                " No collateral data",
                theme::dim(),
            )));
        }
    }

    lines.push(Line::from(""));
    lines.push(Line::from(Span::styled(" Press C to return", theme::dim())));

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

fn format_usd(val: f64) -> String {
    if val.abs() >= 1_000_000.0 {
        format!("${:.1}M", val / 1_000_000.0)
    } else if val.abs() >= 1_000.0 {
        format!("${:.0}", val)
    } else {
        format!("${:.2}", val)
    }
}

/// Extract f64 from collateral JSON (handles both string and numeric values).
fn coll_f64(coll: &serde_json::Value, key: &str) -> f64 {
    coll.get(key)
        .and_then(|v| v.as_str().and_then(|s| s.parse().ok()).or(v.as_f64()))
        .unwrap_or(0.0)
}

/// Extract f64 from an optional JSON value.
fn val_f64(val: Option<&serde_json::Value>, key: &str) -> Option<f64> {
    let obj = val?;
    let v = obj.get(key)?;
    v.as_f64()
        .or_else(|| v.as_str().and_then(|s| s.parse().ok()))
}

fn latency_stats(
    history: &VecDeque<LatencySample>,
    field: fn(&LatencySample) -> i64,
) -> (f64, f64, f64) {
    if history.is_empty() {
        return (0.0, 0.0, 0.0);
    }
    let values: Vec<f64> = history.iter().map(|s| field(s) as f64 / 1000.0).collect(); // us to ms
    let min = values.iter().cloned().fold(f64::MAX, f64::min);
    let max = values.iter().cloned().fold(f64::MIN, f64::max);
    let avg = values.iter().sum::<f64>() / values.len() as f64;
    (min, avg, max)
}

fn detail_line(label: &str, value: String, value_style: ratatui::style::Style) -> Line<'static> {
    Line::from(vec![
        Span::styled(format!(" {label:<14}"), theme::dim()),
        Span::styled(value, value_style),
    ])
}

fn format_compact(val: f64) -> String {
    if val >= 1_000_000.0 {
        format!("{:.1}M", val / 1_000_000.0)
    } else if val >= 1_000.0 {
        format!("{:.1}K", val / 1_000.0)
    } else if val >= 1.0 {
        format!("{:.2}", val)
    } else {
        format!("{:.4}", val)
    }
}

/// Format latency in ms consistently.
fn format_latency(ms: f64) -> String {
    if ms >= 100.0 {
        format!("{:.0}ms", ms)
    } else if ms >= 10.0 {
        format!("{:.1}ms", ms)
    } else {
        format!("{:.2}ms", ms)
    }
}
