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

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::OrderBook;
    let ob = &state.orderbook;
    let total_levels = ob.asks.len() + ob.bids.len();
    let info = if total_levels > 0 {
        let scroll = state.scroll_offset(Panel::OrderBook);
        if scroll > 0 {
            format!("(+{scroll})")
        } else {
            String::new()
        }
    } else {
        String::new()
    };
    let block = theme::panel_block_with_info("Order Book", &info, active);

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

    let mut lines = Vec::new();

    // Header
    lines.push(Line::from(vec![
        Span::styled(format!("{:>10}", "PRICE"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>10}", "SIZE"), theme::header()),
        Span::styled(" ", theme::normal()),
        Span::styled(format!("{:>10}", "TOTAL"), theme::header()),
    ]));

    // Dynamic bar width: fill remaining space after data columns
    let data_cols: u16 = 10 + 1 + 10 + 1 + 10 + 1; // PRICE + SIZE + TOTAL + gaps
    let bar_max = inner.width.saturating_sub(data_cols).max(4) as f64;

    if ob.asks.is_empty() && ob.bids.is_empty() {
        lines.push(Line::from(Span::styled(
            "  Waiting for data...",
            theme::dim(),
        )));
    } else {
        let asks: Vec<(f64, f64)> = ob
            .asks
            .iter()
            .map(|(p, s)| (p.to_f64().unwrap_or(0.0), s.to_f64().unwrap_or(0.0)))
            .collect();
        let bids: Vec<(f64, f64)> = ob
            .bids
            .iter()
            .rev()
            .map(|(p, s)| (p.to_f64().unwrap_or(0.0), s.to_f64().unwrap_or(0.0)))
            .collect();

        let max_rows = (inner.height as usize).saturating_sub(3) / 2;
        let scroll = state.scroll_offset(Panel::OrderBook);

        // Compute cumulative totals for asks (from spread outward)
        let display_asks: Vec<_> = asks.iter().take(max_rows + scroll).collect();
        let mut ask_cumulative = Vec::new();
        let mut cum = 0.0;
        for (_, size) in display_asks.iter() {
            cum += size;
            ask_cumulative.push(cum);
        }
        let max_cum = ask_cumulative.last().copied().unwrap_or(1.0).max(
            bids.iter()
                .take(max_rows + scroll)
                .scan(0.0, |acc, (_, s)| {
                    *acc += s;
                    Some(*acc)
                })
                .last()
                .unwrap_or(1.0),
        );

        // Asks: show N lowest asks, reversed so lowest is near spread
        let ask_start = scroll.min(display_asks.len());
        let visible_asks: Vec<_> = display_asks
            .iter()
            .enumerate()
            .skip(ask_start)
            .take(max_rows)
            .rev()
            .collect();

        for (i, (price, size)) in &visible_asks {
            let cum_total = ask_cumulative.get(*i).copied().unwrap_or(0.0);
            let bar_len = ((cum_total / max_cum) * bar_max) as usize;
            let bar: String = "\u{2588}".repeat(bar_len);
            let intensity = gradient_intensity(*i, visible_asks.len());
            let ask_style = ask_gradient(intensity);
            lines.push(Line::from(vec![
                Span::styled(format!("{price:>10}"), ask_style),
                Span::styled(" ", theme::normal()),
                Span::styled(format!("{size:>10.4}"), theme::secondary()),
                Span::styled(" ", theme::normal()),
                Span::styled(format!("{cum_total:>10.4}"), theme::dim()),
                Span::styled(
                    format!(" {bar}"),
                    ratatui::style::Style::default().fg(theme::RED),
                ),
            ]));
        }

        // Spread line
        let best_ask = asks.first().map(|(p, _)| *p);
        let best_bid = bids.first().map(|(p, _)| *p);
        if let (Some(ask), Some(bid)) = (best_ask, best_bid) {
            let spread = ask - bid;
            let mid = (ask + bid) / 2.0;
            let pct = if mid > 0.0 {
                (spread / mid) * 100.0
            } else {
                0.0
            };
            lines.push(Line::from(vec![Span::styled(
                format!(
                    "  \u{2500}\u{2500}\u{2500} spread: {spread:.4} ({pct:.2}%) \u{2500}\u{2500}\u{2500}"
                ),
                theme::dim(),
            )]));
        }

        // Bids with cumulative
        let mut bid_cum = 0.0;
        let bid_start = scroll.min(bids.len());
        for (i, (price, size)) in bids.iter().enumerate().skip(bid_start).take(max_rows) {
            bid_cum += size;
            let bar_len = ((bid_cum / max_cum) * bar_max) as usize;
            let bar: String = "\u{2588}".repeat(bar_len);
            let dist = i.saturating_sub(bid_start);
            let intensity = gradient_intensity(dist, max_rows);
            let bid_style = bid_gradient(intensity);
            lines.push(Line::from(vec![
                Span::styled(format!("{price:>10}"), bid_style),
                Span::styled(" ", theme::normal()),
                Span::styled(format!("{size:>10.4}"), theme::secondary()),
                Span::styled(" ", theme::normal()),
                Span::styled(format!("{bid_cum:>10.4}"), theme::dim()),
                Span::styled(
                    format!(" {bar}"),
                    ratatui::style::Style::default().fg(theme::GREEN),
                ),
            ]));
        }
    }

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

/// Returns 0.0 (brightest, near spread) to 1.0 (dimmest, far from spread)
fn gradient_intensity(distance_from_spread: usize, total: usize) -> f64 {
    if total <= 1 {
        return 0.0;
    }
    distance_from_spread as f64 / total as f64
}

fn ask_gradient(intensity: f64) -> ratatui::style::Style {
    // Blend from RED (bright) toward TEXT_DIM as we go further from spread
    let (sr, sg, sb) = theme::rgb_components(theme::RED);
    let (dr, dg, db) = theme::rgb_components(theme::TEXT_DIM);
    let r = lerp(sr as u32, dr as u32, intensity) as u8;
    let g = lerp(sg as u32, dg as u32, intensity) as u8;
    let b = lerp(sb as u32, db as u32, intensity) as u8;
    ratatui::style::Style::default().fg(ratatui::style::Color::Rgb(r, g, b))
}

fn bid_gradient(intensity: f64) -> ratatui::style::Style {
    let (sr, sg, sb) = theme::rgb_components(theme::GREEN);
    let (dr, dg, db) = theme::rgb_components(theme::TEXT_DIM);
    let r = lerp(sr as u32, dr as u32, intensity) as u8;
    let g = lerp(sg as u32, dg as u32, intensity) as u8;
    let b = lerp(sb as u32, db as u32, intensity) as u8;
    ratatui::style::Style::default().fg(ratatui::style::Color::Rgb(r, g, b))
}

fn lerp(a: u32, b: u32, t: f64) -> u32 {
    let t = t.clamp(0.0, 1.0);
    ((a as f64) * (1.0 - t) + (b as f64) * t) as u32
}
