use ratatui::layout::Rect;
use ratatui::style::Modifier;
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::Funding;
    let count = state.all_mark_prices.len();
    let info = if count > 0 {
        let scroll = state.scroll_offset(Panel::Funding);
        if scroll > 0 {
            format!("({}/{count})", scroll + 1)
        } else {
            format!("({count})")
        }
    } else {
        String::new()
    };
    let block = theme::panel_block_divided_with_info("Funding Rates", &info, active);
    let inner = block.inner(area);
    f.render_widget(block, area);

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

    if state.all_mark_prices.is_empty() {
        lines.push(Line::from(Span::styled(
            " Waiting for mark price data...",
            theme::dim(),
        )));
        let paragraph = Paragraph::new(lines);
        f.render_widget(paragraph, inner);
        return;
    }

    // Column header
    lines.push(Line::from(vec![
        Span::styled(" ", theme::dim()),
        Span::styled(format!("{:<12}", "SYMBOL"), theme::dim()),
        Span::styled(format!(" {:>9}", "RATE"), theme::dim()),
        Span::styled(format!(" {:>7}", "APR"), theme::dim()),
        Span::styled(format!(" {:>9}", "BASIS"), theme::dim()),
        Span::styled(format!(" {:>6}", "NEXT"), theme::dim()),
        Span::styled(format!(" {:>8}", "24H VOL"), theme::dim()),
        Span::styled(format!(" {:>8}", "OI"), theme::dim()),
        Span::styled(format!(" {:>12}", "CARRY"), theme::dim()),
    ]));

    // Parse and sort mark prices by absolute funding rate (highest first)
    let mut entries: Vec<FundingEntry> = state
        .all_mark_prices
        .iter()
        .filter_map(parse_entry)
        .collect();
    entries.sort_by(|a, b| {
        b.funding_rate
            .abs()
            .partial_cmp(&a.funding_rate.abs())
            .unwrap_or(std::cmp::Ordering::Equal)
    });

    let now_ms = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_millis() as u64;

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

    for (i, entry) in entries.iter().skip(scroll).take(max_rows).enumerate() {
        let is_current = entry.symbol == state.symbol;

        // Funding rate color: green if longs pay (positive), red if shorts pay (negative)
        let rate_color = if entry.funding_rate >= 0.0 {
            theme::GREEN
        } else {
            theme::RED
        };

        // Annualized: rate * 24 (1h intervals) * 365
        let apr = entry.funding_rate * 24.0 * 365.0;

        // Basis spread: mark - index (positive = premium, negative = discount)
        let basis_str = if entry.index_price > 0.0 {
            let basis_bps = ((entry.mark_price - entry.index_price) / entry.index_price) * 10000.0;
            format!("{:+.1}bp", basis_bps)
        } else {
            "-".to_string()
        };
        let basis_color = if entry.mark_price >= entry.index_price {
            theme::GREEN
        } else {
            theme::RED
        };

        // Countdown to next funding (timestamps are in milliseconds)
        let countdown = if entry.next_funding_ts > now_ms {
            let remaining_secs = (entry.next_funding_ts - now_ms) / 1000;
            let h = remaining_secs / 3600;
            let m = (remaining_secs % 3600) / 60;
            format!("{h}h{m:02}m")
        } else {
            "now".to_string()
        };

        // Compact symbol (strip _USDC_PERP → _PERP for readability)
        let display_sym = entry
            .symbol
            .replace("_USDC_PERP", "_PP")
            .replace("_USDC", "");

        let rate_str = format!("{:+.4}%", entry.funding_rate * 100.0);
        let apr_str = format!("{:+.0}%", apr * 100.0);

        // Volume from tickers
        let vol_str = state
            .all_tickers
            .get(&entry.symbol)
            .and_then(|t| {
                t.get("volume").and_then(|v| {
                    v.as_str()
                        .and_then(|s| s.parse::<f64>().ok())
                        .or(v.as_f64())
                })
            })
            .map(format_volume)
            .unwrap_or_else(|| "-".to_string());

        // Open interest
        let oi_str = state
            .open_interest
            .get(&entry.symbol)
            .and_then(|v| {
                v.get("openInterest").and_then(|oi| {
                    oi.as_str()
                        .and_then(|s| s.parse::<f64>().ok())
                        .or(oi.as_f64())
                })
            })
            .map(format_volume)
            .unwrap_or_else(|| "-".to_string());

        // 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 base_style = if is_current {
            ratatui::style::Style::default()
                .fg(theme::ACCENT)
                .add_modifier(Modifier::BOLD)
                .bg(bg)
        } else {
            theme::secondary().bg(bg)
        };

        let rate_style = ratatui::style::Style::default().fg(rate_color).bg(bg);

        // Carry trade: short perp (collect funding) + lend spot (collect interest)
        // Only shown when funding > 0 (longs pay shorts) and a lending market exists
        let (carry_str, carry_color) = if entry.funding_rate > 0.0 {
            let base_asset = entry.symbol.split('_').next().unwrap_or("");
            state
                .borrow_lend_markets
                .get(base_asset)
                .and_then(|m| {
                    let lend_rate = m
                        .get("lendInterestRate")
                        .or_else(|| m.get("lend_interest_rate"))
                        .and_then(|v| {
                            v.as_str()
                                .and_then(|s| s.parse::<f64>().ok())
                                .or_else(|| v.as_f64())
                        })
                        .unwrap_or(0.0);
                    if lend_rate > 0.0 {
                        let fund_apr_pct = apr * 100.0;
                        let lend_apr_pct = lend_rate * 100.0;
                        let total = fund_apr_pct + lend_apr_pct;
                        let color = if total > 30.0 {
                            theme::GREEN
                        } else {
                            theme::YELLOW
                        };
                        Some((format!("{total:.0}%"), color))
                    } else {
                        None
                    }
                })
                .unwrap_or((String::new(), theme::TEXT_DIM))
        } else {
            (String::new(), theme::TEXT_DIM)
        };

        let carry_style = ratatui::style::Style::default().fg(carry_color).bg(bg);

        lines.push(
            Line::from(vec![
                Span::styled(
                    if is_current { ">" } else { " " },
                    ratatui::style::Style::default().fg(theme::ACCENT).bg(bg),
                ),
                Span::styled(format!("{:<12}", display_sym), base_style),
                Span::styled(format!(" {:>9}", rate_str), rate_style),
                Span::styled(format!(" {:>7}", apr_str), rate_style),
                Span::styled(
                    format!(" {:>9}", basis_str),
                    ratatui::style::Style::default().fg(basis_color).bg(bg),
                ),
                Span::styled(format!(" {:>6}", countdown), theme::dim().bg(bg)),
                Span::styled(format!(" {:>8}", vol_str), theme::secondary().bg(bg)),
                Span::styled(format!(" {:>8}", oi_str), theme::secondary().bg(bg)),
                Span::styled(format!(" {:>12}", carry_str), carry_style),
            ])
            .style(row_style),
        );
    }

    // Bottom hint
    if lines.len() < inner.height as usize {
        lines.push(Line::from(""));
        lines.push(Line::from(Span::styled(
            " sorted by |rate| -- your basis trade awaits",
            theme::dim(),
        )));
    }

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

struct FundingEntry {
    symbol: String,
    funding_rate: f64,
    mark_price: f64,
    index_price: f64,
    next_funding_ts: u64,
}

fn parse_entry(v: &serde_json::Value) -> Option<FundingEntry> {
    let symbol = v.get("symbol")?.as_str()?.to_string();
    let funding_rate: f64 = v
        .get("fundingRate")
        .or_else(|| v.get("funding_rate"))
        .and_then(|r| {
            r.as_str().or_else(|| r.as_f64().map(|_| "")).and_then(|s| {
                if s.is_empty() {
                    r.as_f64()
                } else {
                    s.parse().ok()
                }
            })
        })?;
    let mark_price: f64 = v
        .get("markPrice")
        .or_else(|| v.get("mark_price"))
        .and_then(|p| {
            p.as_str()
                .and_then(|s| s.parse().ok())
                .or_else(|| p.as_f64())
        })
        .unwrap_or(0.0);
    let index_price: f64 = v
        .get("indexPrice")
        .or_else(|| v.get("index_price"))
        .and_then(|p| {
            p.as_str()
                .and_then(|s| s.parse().ok())
                .or_else(|| p.as_f64())
        })
        .unwrap_or(0.0);
    let next_funding_ts: u64 = v
        .get("nextFundingTimestamp")
        .or_else(|| v.get("next_funding_timestamp"))
        .and_then(|t| {
            t.as_u64()
                .or_else(|| t.as_str().and_then(|s| s.parse().ok()))
        })
        .unwrap_or(0);

    Some(FundingEntry {
        symbol,
        funding_rate,
        mark_price,
        index_price,
        next_funding_ts,
    })
}

fn format_volume(v: f64) -> String {
    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)
    }
}
