//! Generic context menu component for right-click menus.
//!
//! Provides a reusable popup menu with:
//! - Fixed positioning at mouse coordinates
//! - Viewport edge detection to prevent off-screen rendering
//! - Click-outside-to-close behavior
//! - Escape key to close
//! - Dark theme styling
//! - ARIA attributes for accessibility

use leptos::prelude::*;

/// A menu item in the context menu.
#[derive(Clone)]
pub struct ContextMenuItem {
    /// The label displayed for this menu item.
    pub label: String,
    /// The action identifier passed to the on_select callback.
    pub action: String,
}

/// Context menu component that displays at a specific position.
///
/// Props:
/// - `open`: Whether the menu is currently visible
/// - `position`: The (x, y) position in client coordinates where the menu should appear
/// - `header`: Optional header text displayed at the top (e.g., username)
/// - `items`: The menu items to display
/// - `on_select`: Callback when a menu item is selected, receives the action string
/// - `on_close`: Callback when the menu should close (click outside, escape, etc.)
#[component]
pub fn ContextMenu(
    /// Whether the menu is visible.
    #[prop(into)]
    open: Signal<bool>,
    /// Position (x, y) in client coordinates.
    #[prop(into)]
    position: Signal<(f64, f64)>,
    /// Optional header text (e.g., username) displayed above the menu items.
    #[prop(optional, into)]
    header: Option<Signal<Option<String>>>,
    /// Menu items to display.
    #[prop(into)]
    items: Signal<Vec<ContextMenuItem>>,
    /// Called when an item is selected with the action string.
    #[prop(into)]
    on_select: Callback<String>,
    /// Called when the menu should close.
    #[prop(into)]
    on_close: Callback<()>,
) -> impl IntoView {
    let menu_ref = NodeRef::<leptos::html::Div>::new();

    // Calculate adjusted position to stay within viewport
    let adjusted_style = move || {
        let (x, y) = position.get();
        let menu_width = 150.0; // min-w-[150px]
        let menu_height = 100.0; // approximate

        #[cfg(feature = "hydrate")]
        let (viewport_width, viewport_height) = {
            let window = web_sys::window().unwrap();
            (
                window.inner_width().unwrap().as_f64().unwrap_or(800.0),
                window.inner_height().unwrap().as_f64().unwrap_or(600.0),
            )
        };
        #[cfg(not(feature = "hydrate"))]
        let (viewport_width, viewport_height) = (800.0, 600.0);

        // Adjust position to keep menu within viewport
        let adjusted_x = if x + menu_width > viewport_width {
            (x - menu_width).max(0.0)
        } else {
            x
        };
        let adjusted_y = if y + menu_height > viewport_height {
            (y - menu_height).max(0.0)
        } else {
            y
        };

        format!(
            "position: fixed; left: {}px; top: {}px; z-index: 40;",
            adjusted_x, adjusted_y
        )
    };

    // Click outside handler - use Effect with cleanup to properly remove handlers
    #[cfg(feature = "hydrate")]
    {
        use std::cell::RefCell;
        use std::rc::Rc;
        use wasm_bindgen::{JsCast, closure::Closure};

        // Store closures so we can remove them on cleanup
        let mousedown_closure: Rc<RefCell<Option<Closure<dyn Fn(web_sys::MouseEvent)>>>> =
            Rc::new(RefCell::new(None));
        let keydown_closure: Rc<RefCell<Option<Closure<dyn Fn(web_sys::KeyboardEvent)>>>> =
            Rc::new(RefCell::new(None));

        let mousedown_closure_clone = mousedown_closure.clone();
        let keydown_closure_clone = keydown_closure.clone();

        Effect::new(move |_| {
            let window = web_sys::window().unwrap();

            // Clean up previous handlers first
            if let Some(old_handler) = mousedown_closure_clone.borrow_mut().take() {
                let _ = window.remove_event_listener_with_callback(
                    "mousedown",
                    old_handler.as_ref().unchecked_ref(),
                );
            }
            if let Some(old_handler) = keydown_closure_clone.borrow_mut().take() {
                let _ = window.remove_event_listener_with_callback(
                    "keydown",
                    old_handler.as_ref().unchecked_ref(),
                );
            }

            // Only add handlers when menu is open
            if !open.get() {
                return;
            }

            let Some(menu_el) = menu_ref.get() else {
                return;
            };

            let on_close = on_close.clone();
            let menu_el: web_sys::HtmlElement = menu_el.into();
            let menu_el_clone = menu_el.clone();

            // Mousedown handler for click-outside detection
            let handler =
                Closure::<dyn Fn(web_sys::MouseEvent)>::new(move |ev: web_sys::MouseEvent| {
                    if let Some(target) = ev.target() {
                        if let Ok(target_el) = target.dyn_into::<web_sys::Node>() {
                            if !menu_el_clone.contains(Some(&target_el)) {
                                on_close.run(());
                            }
                        }
                    }
                });

            let _ = window
                .add_event_listener_with_callback("mousedown", handler.as_ref().unchecked_ref());
            *mousedown_closure_clone.borrow_mut() = Some(handler);

            // Escape key handler
            let on_close_esc = on_close.clone();
            let keydown_handler = Closure::<dyn Fn(web_sys::KeyboardEvent)>::new(
                move |ev: web_sys::KeyboardEvent| {
                    if ev.key() == "Escape" {
                        on_close_esc.run(());
                        ev.prevent_default();
                    }
                },
            );
            let _ = window.add_event_listener_with_callback(
                "keydown",
                keydown_handler.as_ref().unchecked_ref(),
            );
            *keydown_closure_clone.borrow_mut() = Some(keydown_handler);
        });
    }

    view! {
        <Show when=move || open.get()>
            <div
                node_ref=menu_ref
                class="bg-gray-800/95 backdrop-blur-sm rounded-lg shadow-lg border border-gray-700 py-1 min-w-[150px]"
                style=adjusted_style
                role="menu"
                aria-label="Context menu"
            >
                // Header with username and divider
                {move || {
                    header.and_then(|h| h.get()).map(|header_text| {
                        view! {
                            <div class="px-4 py-2 text-center">
                                <div class="text-sm font-semibold text-white">{header_text}</div>
                            </div>
                            <hr class="border-gray-600 mx-2" />
                        }
                    })
                }}
                <For
                    each=move || items.get()
                    key=|item| item.action.clone()
                    children=move |item| {
                        let action = item.action.clone();
                        let on_select = on_select.clone();
                        let on_close = on_close.clone();
                        view! {
                            <button
                                class="w-full text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-700 hover:text-white focus:bg-gray-700 focus:text-white focus:outline-none transition-colors"
                                role="menuitem"
                                on:click=move |_| {
                                    on_select.run(action.clone());
                                    on_close.run(());
                                }
                            >
                                {item.label}
                            </button>
                        }
                    }
                />
            </div>
        </Show>
    }
}
