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

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

pub fn render(f: &mut Frame, area: Rect, state: &AppState) {
    let block = Block::default()
        .borders(Borders::BOTTOM)
        .border_style(theme::border(false))
        .style(ratatui::style::Style::default().bg(theme::BG));

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

    // Connection status dot
    let (conn_dot, conn_style) = if state.ws_connected {
        ("\u{25cf}", theme::status_ok())
    } else if state.ws_reconnect_count > 0 {
        ("\u{25cf}", theme::status_warn())
    } else {
        ("\u{25cb}", theme::status_err())
    };

    // Data staleness check — warn if no WS update in 10s
    let stale = state
        .ws_last_update
        .map(|t| t.elapsed() > std::time::Duration::from_secs(10))
        .unwrap_or(false);
    let (dot, dot_style) = if stale {
        ("\u{25cf}", theme::status_warn())
    } else {
        (conn_dot, conn_style)
    };

    // Line 1: branding + symbol + price + change% + H/L + volume + funding
    let mut line1 = vec![
        Span::styled(" ", theme::dim()),
        Span::styled(dot, dot_style),
        Span::styled(" bpx", theme::highlight()),
        Span::styled("  ", theme::dim()),
        Span::styled(&state.symbol, theme::highlight()),
        Span::styled("  ", theme::normal()),
    ];

    if let Some(ref ticker) = state.ticker {
        let price = ticker
            .get("lastPrice")
            .or_else(|| ticker.get("c"))
            .and_then(|v| v.as_str())
            .unwrap_or("-");
        let change_pct = ticker
            .get("priceChangePercent")
            .or_else(|| ticker.get("P"))
            .and_then(|v| v.as_str())
            .unwrap_or("0");
        let high = ticker
            .get("highPrice")
            .or_else(|| ticker.get("h"))
            .and_then(|v| v.as_str())
            .unwrap_or("-");
        let low = ticker
            .get("lowPrice")
            .or_else(|| ticker.get("l"))
            .and_then(|v| v.as_str())
            .unwrap_or("-");
        let vol = ticker
            .get("volume")
            .or_else(|| ticker.get("v"))
            .and_then(|v| v.as_str())
            .unwrap_or("-");

        let pct_val: f64 = change_pct.parse().unwrap_or(0.0);
        let pct_style = if pct_val >= 0.0 {
            theme::bid()
        } else {
            theme::ask()
        };

        line1.extend([
            Span::styled(price, theme::normal()),
            Span::styled("  ", theme::normal()),
            Span::styled(format!("{:+.2}%", pct_val), pct_style),
            Span::styled("  H:", theme::dim()),
            Span::styled(high, theme::normal()),
            Span::styled("  L:", theme::dim()),
            Span::styled(low, theme::normal()),
            Span::styled("  V:", theme::dim()),
            Span::styled(format_volume(vol), theme::normal()),
        ]);

        if let Some(ref mp) = state.mark_price {
            if let Some(rate) = mp
                .get("fundingRate")
                .or_else(|| mp.get("r"))
                .and_then(|v| v.as_str())
            {
                if let Ok(r) = rate.parse::<f64>() {
                    let rate_style = if r >= 0.0 { theme::bid() } else { theme::ask() };
                    line1.push(Span::styled("  F:", theme::dim()));
                    line1.push(Span::styled(format!("{:.4}%", r * 100.0), rate_style));
                }
            }
        }
    } else {
        line1.push(Span::styled("Loading...", theme::dim()));
    }

    // Line 2: mark price, index price, basis, funding countdown
    let mut line2: Vec<Span> = vec![Span::styled("  ", theme::dim())];

    let mp_data = state.mark_price.as_ref();
    let rest_fallback = state.all_mark_prices.iter().find(|v| {
        v.get("symbol")
            .and_then(|s| s.as_str())
            .is_some_and(|s| s == state.symbol)
    });

    let mark_price = extract_f64(mp_data, &["p", "markPrice", "mark_price"])
        .or_else(|| extract_f64(rest_fallback, &["markPrice", "mark_price"]));
    let index_price = extract_f64(mp_data, &["i", "indexPrice", "index_price"])
        .or_else(|| extract_f64(rest_fallback, &["indexPrice", "index_price"]));
    let funding_rate = extract_f64(mp_data, &["f", "fundingRate", "funding_rate"])
        .or_else(|| extract_f64(rest_fallback, &["fundingRate", "funding_rate"]));
    let next_funding_ts = extract_u64(
        mp_data,
        &["n", "nextFundingTimestamp", "next_funding_timestamp"],
    )
    .or_else(|| {
        extract_u64(
            rest_fallback,
            &["nextFundingTimestamp", "next_funding_timestamp"],
        )
    });

    if let Some(mp) = mark_price {
        line2.push(Span::styled("Mark:", theme::dim()));
        line2.push(Span::styled(
            format!(" {}", format_price(mp)),
            theme::normal(),
        ));
    }
    if let Some(ip) = index_price {
        line2.push(Span::styled("  Index:", theme::dim()));
        line2.push(Span::styled(
            format!(" {}", format_price(ip)),
            theme::normal(),
        ));
    }
    if let (Some(mp), Some(ip)) = (mark_price, index_price) {
        if ip > 0.0 {
            let basis_pct = ((mp - ip) / ip) * 100.0;
            let basis_style = if basis_pct >= 0.0 {
                theme::bid()
            } else {
                theme::ask()
            };
            line2.push(Span::styled("  Basis:", theme::dim()));
            line2.push(Span::styled(format!(" {:+.4}%", basis_pct), basis_style));
        }
    }
    if let Some(fr) = funding_rate {
        if fr != 0.0 {
            let apr = fr * 24.0 * 365.0 * 100.0;
            let rate_style = if fr >= 0.0 {
                theme::bid()
            } else {
                theme::ask()
            };
            line2.push(Span::styled("  Funding:", theme::dim()));
            line2.push(Span::styled(
                format!(" {:+.4}% (APR {:+.0}%)", fr * 100.0, apr),
                rate_style,
            ));
        }
    }
    if let Some(ts) = next_funding_ts {
        if ts > 0 {
            let now_ms = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap_or_default()
                .as_millis() as u64;
            if ts > now_ms {
                let remaining_secs = (ts - now_ms) / 1000;
                let h = remaining_secs / 3600;
                let m = (remaining_secs % 3600) / 60;
                line2.push(Span::styled(format!("  Next: {h}h {m:02}m"), theme::dim()));
            }
        }
    }

    // Open interest for current symbol
    if let Some(oi_val) = state.open_interest.get(&state.symbol).and_then(|v| {
        v.get("openInterest").and_then(|oi| {
            oi.as_str()
                .and_then(|s| s.parse::<f64>().ok())
                .or(oi.as_f64())
        })
    }) {
        line2.push(Span::styled("  OI:", theme::dim()));
        line2.push(Span::styled(
            format!(" {}", format_compact(oi_val)),
            theme::normal(),
        ));
    }

    let info = Paragraph::new(vec![Line::from(line1), Line::from(line2)]);
    f.render_widget(info, inner);
}

fn format_volume(vol: &str) -> String {
    if let Ok(v) = vol.parse::<f64>() {
        if v >= 1_000_000.0 {
            format!("{:.1}M", v / 1_000_000.0)
        } else if v >= 1_000.0 {
            format!("{:.1}K", v / 1_000.0)
        } else {
            format!("{:.1}", v)
        }
    } else {
        vol.to_string()
    }
}

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 {
        format!("{:.1}", val)
    }
}

fn format_price(val: f64) -> String {
    if val >= 100.0 {
        format!("{:.2}", val)
    } else if val >= 1.0 {
        format!("{:.4}", val)
    } else {
        format!("{:.6}", val)
    }
}

/// Extract an f64 from a JSON value, trying multiple field names.
fn extract_f64(val: Option<&serde_json::Value>, keys: &[&str]) -> Option<f64> {
    let obj = val?;
    for key in keys {
        if let Some(v) = obj.get(*key) {
            if let Some(f) = v.as_f64() {
                return Some(f);
            }
            if let Some(s) = v.as_str() {
                if let Ok(f) = s.parse::<f64>() {
                    return Some(f);
                }
            }
        }
    }
    None
}

/// Extract a u64 from a JSON value, trying multiple field names.
fn extract_u64(val: Option<&serde_json::Value>, keys: &[&str]) -> Option<u64> {
    let obj = val?;
    for key in keys {
        if let Some(v) = obj.get(*key) {
            if let Some(n) = v.as_u64() {
                return Some(n);
            }
            if let Some(s) = v.as_str() {
                if let Ok(n) = s.parse::<u64>() {
                    return Some(n);
                }
            }
        }
    }
    None
}
