use ruin_app::prelude::*;

#[ruin_runtime::async_main]
async fn main() -> ruin_app::Result<()> {
    App::new()
        .window(
            Window::new()
                .title("RUIN Widget Refs and Commands")
                .app_id("dev.ruin.widget-refs")
                .size(1100.0, 760.0),
        )
        .mount(view! {
            WidgetRefsAndCommandsDemo() {}
        })
        .run()
        .await
}

#[component]
fn WidgetRefsAndCommandsDemo() -> impl IntoView {
    let selected_index = use_signal(|| 0_usize);
    let last_command = use_signal(|| "No command has been run yet.".to_string());
    let results_scroll = use_signal(|| 0.0_f32);
    let results_list = use_widget_ref::<ScrollBoxWidget>();

    use_window_title({
        let selected_index = selected_index.clone();
        move || format!("RUIN Commands ({})", COMMANDS[selected_index.get()].title)
    });

    use_shortcut(
        Shortcut::new(Key::Character('k')).with_ctrl(),
        ShortcutScope::Application,
        {
            let last_command = last_command.clone();
            move || {
                let _ = last_command.set(
                    "Application shortcut fired. Click inside the command list, then use \
                     ArrowUp / ArrowDown / Enter."
                        .to_string(),
                );
            }
        },
    );

    use_shortcut(
        Shortcut::new(Key::Enter),
        ShortcutScope::FocusedWithin(results_list.focus_scope()),
        {
            let selected_index = selected_index.clone();
            let last_command = last_command.clone();
            move || {
                let command = &COMMANDS[selected_index.get()];
                let _ = last_command.set(format!("Executed: {}", command.title));
            }
        },
    );

    let selected = &COMMANDS[selected_index.get()];

    view! {
        column(gap = 16.0, padding = 24.0) {
            text(role = TextRole::Heading(1), size = 32.0, weight = FontWeight::Semibold) {
                "Widget refs and commands"
            }

            text(color = colors::muted(), wrap = TextWrap::Word) {
                "This is a smaller truthful slice of example 02: the results list installs a widget \
                 ref-backed focus scope, typed shortcuts are explicit about application vs focused-within \
                 behavior, and the scroll box keeps its own default arrow-key scrolling."
            }

            block(
                padding = 16.0,
                gap = 10.0,
                background = surfaces::raised(),
                border_radius = 12.0,
            ) {
                text(size = 18.0, weight = FontWeight::Semibold) { "Shortcuts" }
                text(color = colors::muted(), wrap = TextWrap::Word) {
                    "Ctrl+K writes a global status message. Click any command row below to focus the \
                     list, then use ArrowUp / ArrowDown / Enter. The explicit shortcuts now override \
                     the scroll box's default arrow-key scrolling in this focused scope."
                }
            }

            row(gap = 16.0) {
                block(
                    flex = 1.0,
                    padding = 16.0,
                    gap = 10.0,
                    background = surfaces::raised(),
                    border_radius = 12.0,
                ) {
                    text(size = 18.0, weight = FontWeight::Semibold) { "Commands" }

                    button(
                        on_press = {
                            let selected_index = selected_index.clone();
                            let last_command = last_command.clone();
                            move |_| {
                                let command = &COMMANDS[selected_index.get()];
                                let _ = last_command.set(format!("Executed: {}", command.title));
                            }
                        },
                    ) {
                        "Run selected command"
                    }

                    scroll_box(
                        widget_ref = results_list.clone(),
                        offset_y = results_scroll.clone(),
                        height = 420.0,
                        padding = 12.0,
                        background = surfaces::canvas(),
                        border_radius = 10.0,
                        border = (2.0, colors::muted()),
                    ) {
                        column(gap = 8.0) {
                            CommandRows(
                                selected_index = selected_index.clone(),
                                results_list = results_list.clone(),
                                results_scroll = results_scroll.clone(),
                            ) {}
                        }
                    }
                }

                block(
                    width = 320.0,
                    padding = 16.0,
                    gap = 10.0,
                    background = surfaces::raised(),
                    border_radius = 12.0,
                ) {
                    text(size = 18.0, weight = FontWeight::Semibold) { "Selection" }
                    text() { "title = "; selected.title }
                    text(color = colors::muted(), wrap = TextWrap::Word) { selected.subtitle }

                    block(
                        padding = 12.0,
                        gap = 8.0,
                        background = surfaces::canvas(),
                        border_radius = 10.0,
                    ) {
                        text(size = 16.0, weight = FontWeight::Semibold) { "Last command" }
                        text(color = colors::muted(), wrap = TextWrap::Word) { last_command.clone() }
                    }
                }
            }
        }
    }
}

#[component]
fn CommandRows(
    selected_index: Signal<usize>,
    results_list: WidgetRef<ScrollBoxWidget>,
    results_scroll: Signal<f32>,
) -> impl IntoView {
    let row0 = use_widget_ref::<BlockWidget>();
    let row1 = use_widget_ref::<BlockWidget>();
    let row2 = use_widget_ref::<BlockWidget>();
    let row3 = use_widget_ref::<BlockWidget>();
    let row4 = use_widget_ref::<BlockWidget>();
    let row5 = use_widget_ref::<BlockWidget>();
    let row6 = use_widget_ref::<BlockWidget>();
    let row7 = use_widget_ref::<BlockWidget>();
    let row8 = use_widget_ref::<BlockWidget>();
    let row9 = use_widget_ref::<BlockWidget>();

    use_shortcut_with_context(
        Shortcut::new(Key::ArrowDown),
        ShortcutScope::FocusedWithin(results_list.focus_scope()),
        {
            let selected_index = selected_index.clone();
            let results_list = results_list.clone();
            let results_scroll = results_scroll.clone();
            let row0 = row0.clone();
            let row1 = row1.clone();
            let row2 = row2.clone();
            let row3 = row3.clone();
            let row4 = row4.clone();
            let row5 = row5.clone();
            let row6 = row6.clone();
            let row7 = row7.clone();
            let row8 = row8.clone();
            let row9 = row9.clone();
            move |interaction_tree| {
                let next_index = (selected_index.get() + 1).min(COMMANDS.len().saturating_sub(1));
                let _ = selected_index.set(next_index);
                match next_index {
                    0 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row0,
                        &results_scroll,
                    ),
                    1 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row1,
                        &results_scroll,
                    ),
                    2 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row2,
                        &results_scroll,
                    ),
                    3 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row3,
                        &results_scroll,
                    ),
                    4 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row4,
                        &results_scroll,
                    ),
                    5 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row5,
                        &results_scroll,
                    ),
                    6 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row6,
                        &results_scroll,
                    ),
                    7 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row7,
                        &results_scroll,
                    ),
                    8 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row8,
                        &results_scroll,
                    ),
                    9 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row9,
                        &results_scroll,
                    ),
                    _ => {}
                }
            }
        },
    );

    use_shortcut_with_context(
        Shortcut::new(Key::ArrowUp),
        ShortcutScope::FocusedWithin(results_list.focus_scope()),
        {
            let selected_index = selected_index.clone();
            let results_list = results_list.clone();
            let results_scroll = results_scroll.clone();
            let row0 = row0.clone();
            let row1 = row1.clone();
            let row2 = row2.clone();
            let row3 = row3.clone();
            let row4 = row4.clone();
            let row5 = row5.clone();
            let row6 = row6.clone();
            let row7 = row7.clone();
            let row8 = row8.clone();
            let row9 = row9.clone();
            move |interaction_tree| {
                let next_index = selected_index.get().saturating_sub(1);
                let _ = selected_index.set(next_index);
                match next_index {
                    0 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row0,
                        &results_scroll,
                    ),
                    1 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row1,
                        &results_scroll,
                    ),
                    2 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row2,
                        &results_scroll,
                    ),
                    3 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row3,
                        &results_scroll,
                    ),
                    4 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row4,
                        &results_scroll,
                    ),
                    5 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row5,
                        &results_scroll,
                    ),
                    6 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row6,
                        &results_scroll,
                    ),
                    7 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row7,
                        &results_scroll,
                    ),
                    8 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row8,
                        &results_scroll,
                    ),
                    9 => scroll_widget_into_view(
                        interaction_tree,
                        &results_list,
                        &row9,
                        &results_scroll,
                    ),
                    _ => {}
                }
            }
        },
    );

    view! {
        column(gap = 8.0) {
            column(gap = 8.0) {
                CommandRow0(selected_index = selected_index.clone(), row_ref = row0.clone()) {}
                CommandRow1(selected_index = selected_index.clone(), row_ref = row1.clone()) {}
                CommandRow2(selected_index = selected_index.clone(), row_ref = row2.clone()) {}
                CommandRow3(selected_index = selected_index.clone(), row_ref = row3.clone()) {}
                CommandRow4(selected_index = selected_index.clone(), row_ref = row4.clone()) {}
            }

            column(gap = 8.0) {
                CommandRow5(selected_index = selected_index.clone(), row_ref = row5.clone()) {}
                CommandRow6(selected_index = selected_index.clone(), row_ref = row6.clone()) {}
                CommandRow7(selected_index = selected_index.clone(), row_ref = row7.clone()) {}
                CommandRow8(selected_index = selected_index.clone(), row_ref = row8.clone()) {}
                CommandRow9(selected_index = selected_index.clone(), row_ref = row9.clone()) {}
            }
        }
    }
}

macro_rules! command_row_component {
    ($name:ident, $index:expr) => {
        #[component]
        fn $name(selected_index: Signal<usize>, row_ref: WidgetRef<BlockWidget>) -> impl IntoView {
            let command = &COMMANDS[$index];
            let prefix = if selected_index.get() == $index {
                "> "
            } else {
                "  "
            };
            let background = if selected_index.get() == $index {
                surfaces::interactive()
            } else {
                surfaces::interactive_muted()
            };
            view! {
                block(
                    widget_ref = row_ref,
                    padding = 12.0,
                    gap = 6.0,
                    background = background,
                    border_radius = 10.0,
                ) {
                    text(weight = FontWeight::Semibold) {
                        prefix;
                        command.title
                    }

                    text(color = colors::muted(), wrap = TextWrap::Word) {
                        command.subtitle
                    }
                }
            }
        }
    };
}

command_row_component!(CommandRow0, 0);
command_row_component!(CommandRow1, 1);
command_row_component!(CommandRow2, 2);
command_row_component!(CommandRow3, 3);
command_row_component!(CommandRow4, 4);
command_row_component!(CommandRow5, 5);
command_row_component!(CommandRow6, 6);
command_row_component!(CommandRow7, 7);
command_row_component!(CommandRow8, 8);
command_row_component!(CommandRow9, 9);

struct Command {
    title: &'static str,
    subtitle: &'static str,
}

static COMMANDS: &[Command] = &[
    Command {
        title: "Open workspace search",
        subtitle: "Jump into a project-wide search panel with the current repository root prefilled.",
    },
    Command {
        title: "Show diagnostics",
        subtitle: "Collect the latest compile, lint, and runtime diagnostics into a single review list.",
    },
    Command {
        title: "Reload shaders",
        subtitle: "Force a renderer-side asset refresh without restarting the process.",
    },
    Command {
        title: "Toggle layout bounds",
        subtitle: "Render debug outlines for container and text layout boxes.",
    },
    Command {
        title: "Profile text layout",
        subtitle: "Capture a narrow text-layout performance sample for the active window.",
    },
    Command {
        title: "Dump interaction tree",
        subtitle: "Serialize the current focus and hit-test tree for debugging pointer routing.",
    },
    Command {
        title: "Clear transient overlays",
        subtitle: "Dismiss temporary banners, confirmations, and other transient UI state.",
    },
    Command {
        title: "Restart background tasks",
        subtitle: "Cancel and requeue currently tracked background work items.",
    },
    Command {
        title: "Copy scene summary",
        subtitle: "Place a concise scene-graph summary onto the clipboard for issue reports.",
    },
    Command {
        title: "Open command help",
        subtitle: "Explain the currently wired shortcut scopes and default list interactions.",
    },
];

fn scroll_widget_into_view<T>(
    interaction_tree: &InteractionTree,
    scroll_box: &WidgetRef<ScrollBoxWidget>,
    item: &WidgetRef<T>,
    scroll_offset: &Signal<f32>,
) {
    let Some(scroll_box_id) = scroll_box.element_id() else {
        return;
    };
    let Some(item_id) = item.element_id() else {
        return;
    };
    let Some(metrics) = interaction_tree.scroll_metrics_for_element(scroll_box_id) else {
        return;
    };
    let Some(item_rect) = interaction_tree.rect_for_element(item_id) else {
        return;
    };

    let viewport_top = metrics.viewport_rect.origin.y;
    let viewport_bottom = viewport_top + metrics.viewport_rect.size.height;
    let item_top = item_rect.origin.y;
    let item_bottom = item_rect.origin.y + item_rect.size.height;

    scroll_offset.update(|offset| {
        let mut next_offset = *offset;
        if item_top < viewport_top {
            next_offset -= viewport_top - item_top;
        } else if item_bottom > viewport_bottom {
            next_offset += item_bottom - viewport_bottom;
        }
        *offset = next_offset.clamp(0.0, metrics.max_offset_y);
    });
}
