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::Positions;
    let count = state.positions.len();
    let base_title = if active && state.authenticated && count > 0 {
        "Positions  x close"
    } else {
        "Positions"
    };
    let info = if count > 0 {
        format!("({count})")
    } else {
        String::new()
    };
    let block = theme::panel_block_divided_with_info(base_title, &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.positions.is_empty() {
        lines.push(Line::from(Span::styled(
            "  No open positions",
            theme::dim(),
        )));
        let paragraph = Paragraph::new(lines);
        f.render_widget(paragraph, inner);
        return;
    }

    // Header
    lines.push(Line::from(vec![
        Span::styled("  ", theme::header()),
        Span::styled(format!("{:<10}", "SYMBOL"), theme::header()),
        Span::styled(format!("{:>5}", "SIDE"), theme::header()),
        Span::styled(format!("{:>5}", "TAG"), theme::header()),
        Span::styled(format!("{:>8}", "QTY"), theme::header()),
        Span::styled(format!("{:>9}", "ENTRY"), theme::header()),
        Span::styled(format!("{:>9}", "MARK"), theme::header()),
        Span::styled(format!("{:>9}", "PnL"), theme::header()),
        Span::styled(format!(" {:>8}", "NOTIONAL"), theme::header()),
        Span::styled(format!("{:>9}", "LIQ"), theme::header()),
    ]));

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

    for (i, pos) in state
        .positions
        .iter()
        .skip(scroll)
        .take(max_rows)
        .enumerate()
    {
        let symbol = pos.get("symbol").and_then(|v| v.as_str()).unwrap_or("-");
        let display_sym = symbol.replace("_USDC_PERP", "_PP").replace("_USDC", "");
        let qty_str = pos
            .get("netQuantity")
            .or_else(|| pos.get("net_quantity"))
            .and_then(|v| v.as_str())
            .unwrap_or("0");
        let qty: f64 = qty_str.parse().unwrap_or(0.0);
        let side = if qty >= 0.0 { "LONG" } else { "SHORT" };
        let side_style = if qty >= 0.0 {
            theme::bid()
        } else {
            theme::ask()
        };

        let entry_str = pos
            .get("entryPrice")
            .or_else(|| pos.get("entry_price"))
            .and_then(|v| v.as_str())
            .unwrap_or("0");
        let entry: f64 = entry_str.parse().unwrap_or(0.0);

        // Mark price: prefer live WS mark price for the current symbol,
        // otherwise use the position's own mark price from the REST fetch.
        let pos_mark_str = pos
            .get("markPrice")
            .or_else(|| pos.get("mark_price"))
            .and_then(|v| v.as_str())
            .unwrap_or("0");
        let mark: f64 = if symbol == state.symbol {
            // Use live mark price from WebSocket for the active symbol
            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())
                })
                .unwrap_or_else(|| pos_mark_str.parse().unwrap_or(0.0))
        } else {
            pos_mark_str.parse().unwrap_or(0.0)
        };

        // Calculate PnL from mark price: net_quantity * (mark - entry)
        // This matches the web UI's real-time calculation.
        let pnl = if mark > 0.0 && entry > 0.0 {
            qty * (mark - entry)
        } else {
            // Fall back to API's pnlUnrealized if mark price is unavailable
            pos.get("pnlUnrealized")
                .or_else(|| pos.get("pnl_unrealized"))
                .and_then(|v| v.as_str())
                .and_then(|s| s.parse().ok())
                .unwrap_or(0.0)
        };

        let pnl_style = if pnl >= 0.0 {
            theme::bid()
        } else {
            theme::ask()
        };

        // Estimated liquidation price
        let liq_str = pos
            .get("estLiquidationPrice")
            .or_else(|| pos.get("est_liquidation_price"))
            .and_then(|v| v.as_str())
            .unwrap_or("-");

        // Row indicator
        let indicator = if active && i == 0 { ">" } else { " " };

        let mark_str = if mark > 0.0 {
            format_price(mark)
        } else {
            "-".to_string()
        };
        let entry_display = if entry > 0.0 {
            format_price(entry)
        } else {
            "-".to_string()
        };
        let liq_display = format_price_str(liq_str);

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

        // Notional value: qty × mark
        let notional = qty.abs() * mark;
        let notional_str = if notional > 0.0 {
            format_notional(notional)
        } else {
            "-".to_string()
        };

        lines.push(
            Line::from(vec![
                Span::styled(format!("{indicator} "), theme::highlight().bg(bg)),
                Span::styled(format!("{display_sym:<10}"), theme::normal().bg(bg)),
                Span::styled(format!("{side:>5}"), side_style.bg(bg)),
                Span::styled(
                    if state.basis_symbols.contains(symbol) {
                        format!("{:>5}", "BASIS")
                    } else {
                        format!("{:>5}", "")
                    },
                    if state.basis_symbols.contains(symbol) {
                        ratatui::style::Style::default().fg(theme::YELLOW).bg(bg)
                    } else {
                        theme::dim().bg(bg)
                    },
                ),
                Span::styled(
                    format!("{:>8}", format_qty(qty.abs())),
                    theme::normal().bg(bg),
                ),
                Span::styled(format!("{:>9}", entry_display), theme::secondary().bg(bg)),
                Span::styled(format!("{:>9}", mark_str), theme::normal().bg(bg)),
                Span::styled(format!("{:>9}", format_pnl(pnl)), pnl_style.bg(bg)),
                Span::styled(format!(" {:>8}", notional_str), theme::secondary().bg(bg)),
                Span::styled(format!("{:>9}", liq_display), theme::dim().bg(bg)),
            ])
            .style(row_style),
        );
    }

    // Total PnL summary row (if more than 1 position)
    if state.positions.len() > 1 {
        let total_pnl: f64 = state
            .positions
            .iter()
            .filter_map(|pos| {
                let qty_str = pos
                    .get("netQuantity")
                    .or_else(|| pos.get("net_quantity"))
                    .and_then(|v| v.as_str())
                    .unwrap_or("0");
                let qty: f64 = qty_str.parse().ok()?;
                let entry: f64 = pos
                    .get("entryPrice")
                    .or_else(|| pos.get("entry_price"))
                    .and_then(|v| v.as_str())
                    .and_then(|s| s.parse().ok())
                    .unwrap_or(0.0);
                let pos_mark_str = pos
                    .get("markPrice")
                    .or_else(|| pos.get("mark_price"))
                    .and_then(|v| v.as_str())
                    .unwrap_or("0");
                let symbol = pos.get("symbol").and_then(|v| v.as_str()).unwrap_or("");
                let mark: f64 = if symbol == state.symbol {
                    state
                        .mark_price
                        .as_ref()
                        .and_then(|mp| {
                            mp.get("markPrice")
                                .or_else(|| mp.get("mark_price"))
                                .or_else(|| mp.get("p"))
                                .and_then(|v| v.as_str())
                                .and_then(|s| s.parse().ok())
                        })
                        .unwrap_or_else(|| pos_mark_str.parse().unwrap_or(0.0))
                } else {
                    pos_mark_str.parse().unwrap_or(0.0)
                };
                if mark > 0.0 && entry > 0.0 {
                    Some(qty * (mark - entry))
                } else {
                    pos.get("pnlUnrealized")
                        .or_else(|| pos.get("pnl_unrealized"))
                        .and_then(|v| v.as_str())
                        .and_then(|s| s.parse().ok())
                }
            })
            .sum();
        let total_style = if total_pnl >= 0.0 {
            theme::bid()
        } else {
            theme::ask()
        };
        lines.push(Line::from(vec![
            Span::styled(format!("{:>48}", "TOTAL"), theme::dim()),
            Span::styled(format!("{:>9}", format_pnl(total_pnl)), total_style),
        ]));
    }

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

/// Format price: drop trailing zeros, keep 2-6 decimal places depending on magnitude.
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)
    }
}

/// Format a price string, parsing and reformatting if possible.
fn format_price_str(s: &str) -> String {
    if let Ok(v) = s.parse::<f64>() {
        if v > 0.0 {
            return format_price(v);
        }
    }
    s.to_string()
}

/// Format quantity: compact display.
fn format_qty(val: f64) -> String {
    if val >= 1000.0 {
        format!("{:.1}", val)
    } else if val >= 1.0 {
        format!("{:.2}", val)
    } else {
        format!("{:.4}", val)
    }
}

/// Format notional value in compact form (e.g., 4.99K, 152.3K).
fn format_notional(val: f64) -> String {
    if val >= 1_000_000.0 {
        format!("{:.1}M", val / 1_000_000.0)
    } else if val >= 1_000.0 {
        format!("{:.2}K", val / 1_000.0)
    } else {
        format!("{:.1}", val)
    }
}

/// Format PnL with sign and appropriate precision.
fn format_pnl(val: f64) -> String {
    if val.abs() >= 1.0 {
        format!("{:+.2}", val)
    } else {
        format!("{:+.4}", val)
    }
}
