//! Keybindings popup component for customizing emotion hotkeys.

use leptos::prelude::*;

use chattyness_db::models::{EmotionAvailability, EmotionState};

use super::emotion_picker::EmoteListPopup;
use super::keybindings::EmotionKeybindings;
use super::modals::Modal;
use crate::utils::LocalStoragePersist;

/// Keybindings popup component for customizing emotion hotkeys.
///
/// Provides a tabbed interface for configuring keybindings.
/// Currently includes the "Emotions" tab for emotion hotkey customization.
///
/// Props:
/// - `open`: Signal controlling visibility
/// - `keybindings`: RwSignal for keybindings (read/write)
/// - `emotion_availability`: Signal for which emotions are available
/// - `skin_preview_path`: Path to the user's skin layer center asset (for previews)
/// - `on_close`: Callback when popup should close
#[component]
pub fn KeybindingsPopup(
    #[prop(into)] open: Signal<bool>,
    keybindings: RwSignal<EmotionKeybindings>,
    #[prop(into)] emotion_availability: Signal<Option<EmotionAvailability>>,
    #[prop(into)] skin_preview_path: Signal<Option<String>>,
    on_close: Callback<()>,
) -> impl IntoView {
    // Current active tab (for future extensibility)
    let (active_tab, _set_active_tab) = signal("emotions");

    view! {
        <Modal
            open=open
            on_close=on_close
            title="Keybindings"
            title_id="keybindings-modal-title"
            max_width="max-w-2xl"
        >
            // Tab bar
            <div class="flex border-b border-gray-700 mb-4" role="tablist">
                <button
                    type="button"
                    role="tab"
                    aria-selected="true"
                    class="px-4 py-2 font-medium transition-colors border-b-2 -mb-px text-blue-400 border-blue-400"
                >
                    "Emotions"
                </button>
            </div>

            // Tab content
            <Show when=move || active_tab.get() == "emotions">
                <EmotionsTab
                    keybindings=keybindings
                    emotion_availability=emotion_availability
                    skin_preview_path=skin_preview_path
                />
            </Show>

            // Footer with keyboard hint
            <div class="mt-6 pt-4 border-t border-gray-700">
                <p class="text-gray-400 text-sm">
                    "Press "
                    <kbd class="px-1.5 py-0.5 bg-gray-700 rounded text-xs font-mono">"e"</kbd>
                    " + key to trigger emotion (e.g., "
                    <kbd class="px-1.5 py-0.5 bg-gray-700 rounded text-xs font-mono">"e1"</kbd>
                    " for happy)"
                </p>
            </div>
        </Modal>
    }
}

/// Emotions tab content showing the 12 customizable emotion slots.
#[component]
fn EmotionsTab(
    keybindings: RwSignal<EmotionKeybindings>,
    #[prop(into)] emotion_availability: Signal<Option<EmotionAvailability>>,
    #[prop(into)] skin_preview_path: Signal<Option<String>>,
) -> impl IntoView {
    // Track which slot is being edited (None = no picker open)
    let (editing_slot, set_editing_slot) = signal(Option::<usize>::None);

    let on_reset = move |_| {
        keybindings.update(|kb| {
            kb.reset_to_defaults();
            kb.save();
        });
    };

    let on_slot_click = move |index: usize| {
        if index != 0 {
            // Not locked
            set_editing_slot.set(Some(index));
        }
    };

    let on_select = Callback::new(move |(index, emotion): (usize, EmotionState)| {
        keybindings.update(|kb| {
            kb.set_slot(index, emotion);
            kb.save();
        });
        set_editing_slot.set(None);
    });

    let on_close_picker = Callback::new(move |_: ()| {
        set_editing_slot.set(None);
    });

    view! {
        <div id="emotions-panel" role="tabpanel" aria-labelledby="emotions-tab">
            <p class="text-gray-400 text-sm mb-4">
                "Customize which emotion is triggered by each key combination. "
                "The first slot (e1) is always Happy."
            </p>

            // Grid of emotion slots (3 columns x 4 rows)
            <div class="grid grid-cols-3 gap-3 mb-4">
                {(0..12).map(|i| {
                    let current_emotion = Signal::derive(move || keybindings.get().slots[i]);
                    let is_editing = Signal::derive(move || editing_slot.get() == Some(i));
                    view! {
                        <EmotionSlot
                            index=i
                            current_emotion=current_emotion
                            is_editing=is_editing
                            emotion_availability=emotion_availability
                            skin_preview_path=skin_preview_path
                            on_click=on_slot_click
                            on_select=on_select
                            on_close=on_close_picker
                        />
                    }
                }).collect_view()}
            </div>

            // Reset button
            <div class="flex justify-end">
                <button
                    type="button"
                    class="px-4 py-2 text-sm bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors"
                    on:click=on_reset
                >
                    "Reset to Defaults"
                </button>
            </div>
        </div>
    }
}

/// Individual emotion slot with label, current emotion, and inline picker.
#[component]
fn EmotionSlot(
    /// Slot index (0-11).
    index: usize,
    #[prop(into)] current_emotion: Signal<EmotionState>,
    #[prop(into)] is_editing: Signal<bool>,
    #[prop(into)] emotion_availability: Signal<Option<EmotionAvailability>>,
    #[prop(into)] skin_preview_path: Signal<Option<String>>,
    on_click: impl Fn(usize) + Clone + 'static,
    on_select: Callback<(usize, EmotionState)>,
    on_close: Callback<()>,
) -> impl IntoView {
    let is_locked = index == 0;
    let slot_label = EmotionKeybindings::slot_label(index);

    // Local state for the picker filter and selection
    let (filter, set_filter) = signal(String::new());
    let (selected_idx, set_selected_idx) = signal(0usize);
    let input_ref = NodeRef::<leptos::html::Input>::new();

    // Focus input when picker opens
    #[cfg(feature = "hydrate")]
    {
        Effect::new(move |_| {
            if is_editing.get() {
                if let Some(input) = input_ref.get() {
                    let _ = input.focus();
                }
            } else {
                // Reset filter when closed
                set_filter.set(String::new());
                set_selected_idx.set(0);
            }
        });
    }

    let on_click_clone = on_click.clone();
    let handle_click = move |_| {
        on_click_clone(index);
    };

    // Convert emotion name to EmotionState and call the parent callback
    let on_popup_select = {
        let on_select = on_select.clone();
        Callback::new(move |emotion_name: String| {
            if let Ok(emotion_state) = emotion_name.parse::<EmotionState>() {
                on_select.run((index, emotion_state));
            }
        })
    };

    // Filtered emotions for keyboard navigation
    let filtered_count = move || {
        let filter_text = filter.get().to_lowercase();
        super::emotion_picker::EMOTIONS
            .iter()
            .filter(|name| filter_text.is_empty() || name.starts_with(&filter_text))
            .count()
    };

    let on_input = move |ev| {
        let value = event_target_value(&ev);
        set_filter.set(value);
        set_selected_idx.set(0);
    };

    #[cfg(feature = "hydrate")]
    let on_keydown = {
        let on_close = on_close.clone();
        let on_popup_select = on_popup_select.clone();
        move |ev: web_sys::KeyboardEvent| {
            // Stop all key events from bubbling to prevent triggering global keybindings
            ev.stop_propagation();

            let key = ev.key();
            let count = filtered_count();

            if key == "Escape" {
                on_close.run(());
                ev.prevent_default();
                return;
            }

            if key == "ArrowDown" && count > 0 {
                set_selected_idx.update(|idx| {
                    *idx = (*idx + 1) % count;
                });
                ev.prevent_default();
                return;
            }

            if key == "ArrowUp" && count > 0 {
                set_selected_idx.update(|idx| {
                    *idx = if *idx == 0 { count - 1 } else { *idx - 1 };
                });
                ev.prevent_default();
                return;
            }

            if key == "Enter" && count > 0 {
                let filter_text = filter.get_untracked().to_lowercase();
                let idx = selected_idx.get_untracked();
                let emotion = super::emotion_picker::EMOTIONS
                    .iter()
                    .filter(|name| filter_text.is_empty() || name.starts_with(&filter_text))
                    .nth(idx)
                    .map(|s| (*s).to_string());
                if let Some(emotion_name) = emotion {
                    on_popup_select.run(emotion_name);
                }
                ev.prevent_default();
            }
        }
    };

    #[cfg(not(feature = "hydrate"))]
    let on_keydown = move |_ev: leptos::ev::KeyboardEvent| {};

    let on_close_for_backdrop = on_close.clone();

    view! {
        <div class="relative">
            <button
                type="button"
                class=move || format!(
                    "w-full p-3 rounded-lg border transition-colors text-left {}",
                    if is_locked {
                        "bg-gray-900 border-gray-700 cursor-not-allowed opacity-75"
                    } else if is_editing.get() {
                        "bg-gray-700 border-blue-500"
                    } else {
                        "bg-gray-700 border-gray-600 hover:border-gray-500"
                    }
                )
                disabled=is_locked
                on:click=handle_click
                aria-haspopup="listbox"
                aria-expanded=move || is_editing.get().to_string()
            >
                <div class="flex items-center justify-between">
                    <div>
                        <kbd class="px-1.5 py-0.5 bg-gray-600 rounded text-xs font-mono text-gray-300">
                            {slot_label}
                        </kbd>
                        {if is_locked {
                            view! { <span class="ml-2 text-xs text-gray-500">"(locked)"</span> }.into_any()
                        } else {
                            view! { <span></span> }.into_any()
                        }}
                    </div>
                    <div class="flex items-center gap-2">
                        <span class="text-white capitalize">{move || current_emotion.get().to_string()}</span>
                        {if !is_locked {
                            view! {
                                <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
                                </svg>
                            }.into_any()
                        } else {
                            view! { <span></span> }.into_any()
                        }}
                    </div>
                </div>
            </button>

            // Emotion picker using shared EmoteListPopup
            <Show when=move || is_editing.get()>
                <div class="fixed inset-0 z-[60]" on:click=move |_| on_close_for_backdrop.run(())>
                    <div
                        class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
                        on:click=move |ev| ev.stop_propagation()
                    >
                        // Hidden input for keyboard capture
                        <input
                            type="text"
                            class="emotion-picker-filter sr-only"
                            prop:value=move || filter.get()
                            on:input=on_input
                            on:keydown=on_keydown
                            node_ref=input_ref
                            autocomplete="off"
                            aria-label="Filter emotions"
                        />
                        <EmoteListPopup
                            emotion_availability=emotion_availability
                            skin_preview_path=skin_preview_path
                            on_select=on_popup_select
                            on_close=on_close
                            emotion_filter=Signal::derive(move || filter.get())
                            selected_idx=Signal::derive(move || selected_idx.get())
                            show_all_emotions=true
                            popup_style="static"
                        />
                    </div>
                </div>
            </Show>
        </div>
    }
}
