use ruin_app::prelude::*;
use ruin_ui::{BoxShadow, BoxShadowKind, Point, TextFontFamily};

use crate::state::{CalcMode, CalcState};

// ---------------------------------------------------------------------------
// Display panel
// ---------------------------------------------------------------------------

#[component]
pub fn Display(state: CalcState) -> impl IntoView {
    let expression = state.expression.clone();
    let is_error = state.is_error.clone();
    let history = state.history.clone();

    let result_color = use_memo({
        let is_error = is_error.clone();
        move || {
            if is_error.get() {
                Color::rgba(200, 30, 30, 245)
            } else {
                Color::rgba(20, 140, 20, 245)
            }
        }
    });

    let result_text = use_memo({
        let result = state.result.clone();
        move || {
            let r = result.get();
            if r.is_empty() {
                " ".to_string()
            } else {
                format!("= {r}")
            }
        }
    });

    view! {
        block(
            padding = 12.0,
            gap = 8.0,
            // min_height (content) sized to cover scroll_box + expression + result lines
            // so the window auto-sizes correctly despite text min-size being 0.
            // Content: scroll_box(100) + expr_text(31) + result_text(20) + 2×gap(16) = 167
            min_height = 167.0,
            align_self = AlignItems::Stretch,
            align_items = AlignItems::End,
            background = Color::rgba(255, 255, 255, 200),
            border_radius = 12.0,
            border = (1.0, Color::rgba(0, 0, 0, 60)),
            shadow = BoxShadow::new(Point::new(2.0, 2.0), 8.0, 2.0, Color::rgba(0, 0, 0, 40), BoxShadowKind::Outer),
        ) {
            block(flex = 1.0, min_height = 0.0) {}

            // History (oldest at top, newest at bottom, scrollable)
            scroll_box(
                height = 100.0,
                offset_y = state.history_scroll.clone(),
                align_self = AlignItems::Stretch,
            ) {
                column(padding = 4.0, gap = 2.0, align_items = AlignItems::End) {
                    history.with(|entries| {
                        entries
                            .iter()
                            .rev()
                            .map(|e| {
                                let expr = e.expression.clone();
                                let res = e.result.clone();
                                let s = state.clone();
                                let res_for_press = res.clone();
                                view! {
                                    button(
                                        align_self = AlignItems::End,
                                        padding = 4.0,
                                        background = Color::rgba(0, 0, 0, 0),
                                        border_radius = 4.0,
                                        on_press = move |_| s.use_history_result(res_for_press.clone()),
                                    ) {
                                        row(gap = 6.0, align_self = AlignItems::End) {
                                            text(size = 12.0, color = Color::rgba(0, 0, 0, 200), font_family = TextFontFamily::Monospace, wrap = TextWrap::None, weight = FontWeight::Normal) {
                                                expr.clone()
                                            }
                                            text(size = 12.0, color = Color::rgba(20, 130, 20, 230), font_family = TextFontFamily::Monospace, wrap = TextWrap::None, weight = FontWeight::Normal) {
                                                format!("= {res}")
                                            }
                                        }
                                    }
                                }
                            })
                            .collect::<Vec<_>>()
                    })
                }
            }

            // Current expression
            text(
                size = 26.0,
                font_family = TextFontFamily::Monospace,
                color = Color::rgba(0, 0, 0, 245),
                wrap = TextWrap::None,
                weight = FontWeight::Normal,
                pointer_events = false,
            ) {
                expression.get()
            }

            // Result / error line
            if is_error.get() {
                view! {
                    text(
                        size = 16.0,
                        font_family = TextFontFamily::Monospace,
                        color = result_color.get(),
                        wrap = TextWrap::None,
                        weight = FontWeight::Normal,
                        pointer_events = false,
                    ) {
                        result_text.get()
                    }
                }
            } else {
                view! {
                    block(min_height = 0.0) {}
                }
            }
        }
    }
}

// ---------------------------------------------------------------------------
// Mode toggle
// ---------------------------------------------------------------------------

#[component]
pub fn ModeToggle(state: CalcState) -> impl IntoView {
    let mode = state.mode.clone();

    view! {
        row(gap = 6.0, align_self = AlignItems::Stretch) {
            toggle_button("Basic", mode.get() == CalcMode::Basic, {
                let state = state.clone();
                move || state.toggle_mode()
            })
            toggle_button("Scientific", mode.get() == CalcMode::Scientific, {
                let state = state.clone();
                move || state.toggle_mode()
            })
        }
    }
}

// ---------------------------------------------------------------------------
// Button helpers
// ---------------------------------------------------------------------------

fn calc_button(label: String, bg: Color, on_press: impl Fn() + 'static) -> View {
    let widget_ref = use_widget_ref::<BlockWidget>();
    let interaction = use_interaction_state(widget_ref.clone());

    button()
        .widget_ref(widget_ref)
        .flex(1.0)
        .min_width(54.0)
        // min_height = content height for 20px bold text (20 * 1.2 = 24).
        // This matches the actual rendered line height so window sizing is accurate
        // and the text is visually centered by the symmetric 8px top/bottom padding.
        .min_height(24.0)
        .padding(Edges::symmetric(8.0, 12.0))
        .align_items(AlignItems::Center)
        .background(interactive_background(bg, interaction))
        .border_radius(8.0)
        .border((0.5, interactive_border(interaction)))
        .shadow(interactive_shadow(interaction))
        .on_press(move |_| on_press())
        .children(
            text()
                .size(20.0)
                .weight(FontWeight::Bold)
                .selectable(false)
                .pointer_events(false)
                .children(label),
        )
}

fn btn_row(buttons: Vec<View>) -> View {
    row().gap(6.0).children(buttons)
}

fn toggle_button(label: &'static str, active: bool, on_press: impl Fn() + 'static) -> View {
    let widget_ref = use_widget_ref::<BlockWidget>();
    let interaction = use_interaction_state(widget_ref.clone());
    let base = if active {
        Color::rgba(60, 100, 220, 180)
    } else {
        Color::rgba(255, 255, 255, 90)
    };

    button()
        .widget_ref(widget_ref)
        .flex(1.0)
        .padding(8.0)
        .min_height(17.0)
        .align_items(AlignItems::Center)
        .background(interactive_background(base, interaction))
        .border_radius(8.0)
        .border((0.5, interactive_border(interaction)))
        .shadow(interactive_shadow(interaction))
        .on_press(move |_| on_press())
        .children(
            text()
                .size(14.0)
                .weight(FontWeight::Bold)
                .selectable(false)
                .pointer_events(false)
                .children(label),
        )
}

fn interactive_background(base: Color, interaction: InteractionState) -> Color {
    if interaction.pressed {
        mix(base, Color::BLACK, 0.22)
    } else if interaction.hovered {
        mix(base, Color::WHITE, 0.22)
    } else {
        base
    }
}

fn interactive_border(interaction: InteractionState) -> Color {
    if interaction.pressed {
        Color::rgba(0, 0, 0, 96)
    } else if interaction.hovered {
        Color::rgba(0, 0, 0, 72)
    } else {
        Color::rgba(0, 0, 0, 35)
    }
}

fn interactive_shadow(interaction: InteractionState) -> BoxShadow {
    if interaction.pressed {
        BoxShadow::new(
            Point::new(0.0, 0.0),
            2.0,
            0.0,
            Color::rgba(0, 0, 0, 36),
            BoxShadowKind::Outer,
        )
    } else if interaction.hovered {
        BoxShadow::new(
            Point::new(0.0, 3.0),
            14.0,
            0.0,
            Color::rgba(0, 0, 0, 42),
            BoxShadowKind::Outer,
        )
    } else {
        BoxShadow::new(
            Point::new(0.0, 1.0),
            6.0,
            0.0,
            Color::rgba(0, 0, 0, 18),
            BoxShadowKind::Outer,
        )
    }
}

fn mix(base: Color, overlay: Color, amount: f32) -> Color {
    let amount = amount.clamp(0.0, 1.0);
    let blend = |from: u8, to: u8| -> u8 {
        ((from as f32) + ((to as f32) - (from as f32)) * amount).round() as u8
    };

    Color::rgba(
        blend(base.r, overlay.r),
        blend(base.g, overlay.g),
        blend(base.b, overlay.b),
        base.a,
    )
}

// ---------------------------------------------------------------------------
// Keypad (basic)
// ---------------------------------------------------------------------------

#[component]
pub fn Keypad(state: CalcState) -> impl IntoView {
    let bg_num = Color::rgba(255, 255, 255, 90);
    let bg_op = Color::rgba(210, 230, 255, 140);
    let bg_eq = Color::rgba(160, 230, 160, 160);
    let bg_fn = Color::rgba(255, 200, 180, 140);

    macro_rules! digit {
        ($ch:expr) => {{
            let s = state.clone();
            let t = $ch.to_string();
            let label = t.clone();
            calc_button(label, bg_num, move || s.input(&t))
        }};
    }
    macro_rules! op {
        ($ch:expr) => {{
            let s = state.clone();
            let t = $ch.to_string();
            let label = t.clone();
            calc_button(label, bg_op, move || s.input(&t))
        }};
    }

    let clear_btn = {
        let s = state.clone();
        calc_button("C".to_string(), bg_fn, move || s.clear())
    };
    let paren_btn = {
        let s = state.clone();
        calc_button("( )".to_string(), bg_num, move || s.input_paren())
    };
    let pct_btn = {
        let s = state.clone();
        calc_button("%".to_string(), bg_num, move || s.input("%"))
    };
    let bs_btn = {
        let s = state.clone();
        calc_button("⌫".to_string(), bg_fn, move || s.backspace())
    };
    let dot_btn = {
        let s = state.clone();
        calc_button(".".to_string(), bg_num, move || s.input("."))
    };
    let eq_btn = {
        let s = state.clone();
        calc_button("=".to_string(), bg_eq, move || s.evaluate())
    };

    view! {
        column(gap = 6.0, align_self = AlignItems::Stretch) {
            btn_row(vec![clear_btn, paren_btn, pct_btn, bs_btn])
            btn_row(vec![digit!('7'), digit!('8'), digit!('9'), op!('÷')])
            btn_row(vec![digit!('4'), digit!('5'), digit!('6'), op!('×')])
            btn_row(vec![digit!('1'), digit!('2'), digit!('3'), op!('-')])
            btn_row(vec![digit!('0'), dot_btn, eq_btn, op!('+')])
        }
    }
}

// ---------------------------------------------------------------------------
// Scientific panel
// ---------------------------------------------------------------------------

#[component]
pub fn ScientificPanel(state: CalcState) -> impl IntoView {
    let bg = Color::rgba(230, 215, 255, 140);

    macro_rules! sci {
        ($label:expr, $insert:expr) => {{
            let s = state.clone();
            let ins: &'static str = $insert;
            calc_button($label.to_string(), bg, move || s.input(ins))
        }};
    }

    let sq_btn = {
        let s = state.clone();
        calc_button("x²".to_string(), bg, move || {
            s.expression.update(|e| {
                let wrapped = format!("({e})^2");
                *e = wrapped;
            });
        })
    };

    view! {
        column(gap = 6.0, align_self = AlignItems::Stretch) {
            btn_row(vec![sci!("sin(", "sin("), sci!("cos(", "cos("), sci!("tan(", "tan("), sci!("asin(", "asin(")])
            btn_row(vec![sci!("log(", "log("), sci!("ln(", "ln("), sci!("√(", "√("), sq_btn])
            btn_row(vec![sci!("xʸ", "^"), sci!("π", "π"), sci!("e", "e"), sci!("fact(", "fact(")])
        }
    }
}
