use super::symbols_data::{get_symbols, SymbolCategory, SymbolObject};
use gtk4::prelude::*;
use gtk4::{
    gio, glib, Box, CustomFilter, FilterListModel, GridView, Orientation, PolicyType,
    ScrolledWindow, SignalListItemFactory, SingleSelection, ToggleButton,
};
use std::cell::RefCell;
use std::rc::Rc;

pub fn create_symbols_grid(search_entry: &gtk4::SearchEntry) -> Box {
    let container = Box::new(Orientation::Horizontal, 0);
    container.set_css_classes(&["emoji-page"]);

    // 1. Sidebar (Categories)
    let sidebar = Box::new(Orientation::Vertical, 6);
    sidebar.set_margin_start(6);
    sidebar.set_margin_end(6);
    sidebar.set_margin_top(6);
    sidebar.set_margin_bottom(6);

    // 2. Store
    let store = gio::ListStore::new::<SymbolObject>();
    store.extend_from_slice(&get_symbols());

    // 3. Filter
    let current_category = Rc::new(RefCell::new(SymbolCategory::Arrows));
    let current_query = Rc::new(RefCell::new(String::new()));

    let filter = CustomFilter::new(glib::clone!(
        #[strong]
        current_category,
        #[strong]
        current_query,
        move |obj| {
            let sym = obj.downcast_ref::<SymbolObject>().unwrap();
            let query = current_query.borrow();

            if !query.is_empty() {
                return sym.name().to_lowercase().contains(query.as_str());
            }

            sym.category() == *current_category.borrow()
        }
    ));

    let filter_model = FilterListModel::new(Some(store), Some(filter.clone()));
    let selection_model = SingleSelection::new(Some(filter_model));

    // Connect Search with debounce (150ms)
    let debounce_source: Rc<RefCell<Option<glib::SourceId>>> = Rc::new(RefCell::new(None));
    search_entry.connect_search_changed(glib::clone!(
        #[weak]
        filter,
        #[strong]
        current_query,
        #[strong]
        debounce_source,
        move |entry| {
            // Cancel previous debounce timer if still pending
            if let Some(source_id) = debounce_source.borrow_mut().take() {
                let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                    source_id.remove();
                }));
            }

            let query = entry.text().to_string().to_lowercase();
            let current_query_clone = current_query.clone();
            let filter_weak = filter.downgrade();
            let debounce_source_clone = debounce_source.clone();

            let source_id =
                glib::timeout_add_local_once(std::time::Duration::from_millis(150), move || {
                    *debounce_source_clone.borrow_mut() = None;
                    *current_query_clone.borrow_mut() = query;
                    if let Some(f) = filter_weak.upgrade() {
                        f.changed(gtk4::FilterChange::Different);
                    }
                });
            *debounce_source.borrow_mut() = Some(source_id);
        }
    ));

    // Buttons
    let categories = vec![
        ("→", SymbolCategory::Arrows, "Arrows"),
        ("Ω", SymbolCategory::Greek, "Greek"),
        ("∑", SymbolCategory::Math, "Math"),
        ("€", SymbolCategory::Currency, "Currency"),
        ("⌘", SymbolCategory::Tech, "Technical"),
        ("┏", SymbolCategory::BoxDrawing, "Box Drawing"),
        ("★", SymbolCategory::Misc, "Misc"),
    ];

    let mut first_btn: Option<ToggleButton> = None;
    for (icon, cat, tooltip) in categories {
        let btn = ToggleButton::builder()
            .label(icon)
            .tooltip_text(tooltip)
            .css_classes(["category-btn", "flat"])
            .build();

        if let Some(ref first) = first_btn {
            btn.set_group(Some(first));
        } else {
            first_btn = Some(btn.clone());
            btn.set_active(true);
        }

        let cat_val = cat;
        btn.connect_toggled(glib::clone!(
            #[strong]
            current_category,
            #[weak]
            filter,
            move |b| {
                if b.is_active() {
                    *current_category.borrow_mut() = cat_val;
                    filter.changed(gtk4::FilterChange::Different);
                }
            }
        ));
        sidebar.append(&btn);
    }
    container.append(&sidebar);

    // 4. Grid Factory
    let factory = SignalListItemFactory::new();
    factory.connect_setup(move |_factory, item| {
        let item = item.downcast_ref::<gtk4::ListItem>().unwrap();
        let button = gtk4::Button::builder()
            .css_classes(["emoji-btn", "flat"])
            .build();
        item.set_child(Some(&button));
        button.connect_clicked(move |btn| {
            let text = btn.label().unwrap_or_default().to_string();
            super::insert_text(text, crate::close_on_select());
        });
    });

    factory.connect_bind(move |_factory, item| {
        let item = item.downcast_ref::<gtk4::ListItem>().unwrap();
        let button = item.child().unwrap().downcast::<gtk4::Button>().unwrap();
        let entry = item.item().unwrap().downcast::<SymbolObject>().unwrap();
        button.set_label(&entry.char());
    });

    let grid_view = GridView::builder()
        .model(&selection_model)
        .factory(&factory)
        .max_columns(10)
        .min_columns(5)
        .enable_rubberband(false)
        .build();

    let scrolled = ScrolledWindow::builder()
        .child(&grid_view)
        .hscrollbar_policy(PolicyType::Never)
        .hexpand(true)
        .vexpand(true)
        .build();

    container.append(&scrolled);

    // Enter in the search box = select the first visible item and close.
    super::on_search_enter_commit::<SymbolObject, _>(
        search_entry,
        &container,
        &selection_model,
        &debounce_source,
        &current_query,
        &filter,
        |o| o.char(),
    );

    container
}
