// pages/search.rs — Search results page

use gtk4::prelude::*;
use gtk4::{
    glib, Align, Box as GBox, Label, Orientation, ScrolledWindow, Widget,
};
use libadwaita::prelude::*;
use libadwaita::NavigationView;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{mpsc, Arc};
use std::time::Duration;

static SEARCH_GEN: AtomicU64 = AtomicU64::new(0);

use rakuos_packages::NativeApp;
use rakuos_webapps::WebApp;

use super::icon_helper::load_app_icon;

/// Build the search results page. Returns (page widget, list_box, empty_state)
/// so callers can pass them directly to run_search without widget-tree traversal.
pub fn build(_nav: Arc<NavigationView>) -> (Widget, gtk4::ListBox, libadwaita::StatusPage) {
    let scroll = ScrolledWindow::builder()
        .hscrollbar_policy(gtk4::PolicyType::Never)
        .vscrollbar_policy(gtk4::PolicyType::Automatic)
        .vexpand(true)
        .build();

    let outer = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(0)
        .build();
    scroll.set_child(Some(&outer));

    let empty_state = libadwaita::StatusPage::builder()
        .title("Search for Apps")
        .description("Type a name to search the app catalog")
        .icon_name("edit-find-symbolic")
        .build();
    outer.append(&empty_state);

    let list_box = gtk4::ListBox::builder()
        .selection_mode(gtk4::SelectionMode::None)
        .css_classes(vec!["boxed-list".to_string()])
        .margin_top(12)
        .margin_bottom(12)
        .margin_start(16)
        .margin_end(16)
        .build();
    list_box.set_visible(false);
    outer.append(&list_box);

    (scroll.upcast::<Widget>(), list_box, empty_state)
}

/// Run a search and update the results list directly via the provided widgets.
/// Uses a generation counter so only the latest search updates the UI — stale
/// results from a slower previous query are silently discarded.
pub fn run_search(list_box: &gtk4::ListBox, empty: &libadwaita::StatusPage, nav: &Arc<NavigationView>, query: String) {
    let gen = SEARCH_GEN.fetch_add(1, Ordering::SeqCst) + 1;

    if query.trim().is_empty() {
        list_box.set_visible(false);
        empty.set_title("Search for Apps");
        empty.set_description(Some("Type a name to search the app catalog"));
        empty.set_visible(true);
        return;
    }

    // Clear previous results immediately so the list doesn't show stale data.
    while let Some(child) = list_box.first_child() {
        list_box.remove(&child);
    }

    let (tx, rx) = mpsc::channel::<(Vec<NativeApp>, Vec<WebApp>)>();
    let query_display = query.clone();

    std::thread::spawn(move || {
        let packages = rakuos_packages::search(&query).unwrap_or_default();
        let webapps = rakuos_webapps::search(&query);
        let _ = tx.send((packages, webapps));
    });

    let list_c = list_box.clone();
    let empty_c = empty.clone();
    let nav_c = Arc::clone(nav);
    glib::timeout_add_local(Duration::from_millis(50), move || {
        // A newer search has started — discard these results.
        if SEARCH_GEN.load(Ordering::SeqCst) != gen {
            return glib::ControlFlow::Break;
        }
        match rx.try_recv() {
            Ok((packages, webapps)) => {
                // Check generation again after receiving — another search may
                // have started while we were waiting in the channel.
                if SEARCH_GEN.load(Ordering::SeqCst) != gen {
                    return glib::ControlFlow::Break;
                }
                while let Some(child) = list_c.first_child() {
                    list_c.remove(&child);
                }
                if packages.is_empty() && webapps.is_empty() {
                    list_c.set_visible(false);
                    empty_c.set_title("No Results");
                    empty_c.set_description(Some(&format!(
                        "No apps found for \u{201c}{}\u{201d}",
                        query_display
                    )));
                    empty_c.set_visible(true);
                } else {
                    empty_c.set_visible(false);
                    list_c.set_visible(true);
                    for app in packages.iter().take(30) {
                        list_c.append(&build_result_row_native(app, &nav_c));
                    }
                    for app in webapps.iter().take(10) {
                        list_c.append(&build_result_row_webapp(app, &nav_c));
                    }
                }
                glib::ControlFlow::Break
            }
            Err(mpsc::TryRecvError::Empty) => glib::ControlFlow::Continue,
            Err(_) => glib::ControlFlow::Break,
        }
    });
}

fn build_result_row_native(app: &NativeApp, nav: &Arc<NavigationView>) -> Widget {
    let row = GBox::builder()
        .orientation(Orientation::Horizontal)
        .spacing(12)
        .margin_top(8)
        .margin_bottom(8)
        .margin_start(12)
        .margin_end(12)
        .build();

    let icon = load_app_icon(&app.icon_path, &app.icon_url, 48, &app.name);
    row.append(&icon);

    let text = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(2)
        .valign(Align::Center)
        .hexpand(true)
        .build();

    let name_lbl = Label::builder()
        .label(&app.name)
        .halign(Align::Start)
        .css_classes(vec!["heading".to_string()])
        .build();
    let summary_lbl = Label::builder()
        .label(&app.summary)
        .halign(Align::Start)
        .wrap(false)
        .ellipsize(gtk4::pango::EllipsizeMode::End)
        .max_width_chars(50)
        .css_classes(vec!["caption".to_string()])
        .build();
    text.append(&name_lbl);
    text.append(&summary_lbl);
    row.append(&text);

    let source_text = match app.source.as_str() {
        "flatpak" => "Flatpak",
        "terra" => "Terra",
        _ => "RPM",
    };
    let badge = Label::builder()
        .label(source_text)
        .css_classes(vec!["caption".to_string(), "dim-label".to_string()])
        .valign(Align::Center)
        .build();
    row.append(&badge);

    let nav = Arc::clone(nav);
    let id = app.id.clone();
    let name = app.name.clone();
    let summary = app.summary.clone();
    let icon_path = app.icon_path.clone();
    let icon_url = app.icon_url.clone();
    let source = app.source.clone();
    let gesture = gtk4::GestureClick::new();
    gesture.connect_released(move |_, _, _, _| {
        super::detail::push_detail(&nav, &id, &name, &summary, &icon_path, &icon_url, &source);
    });
    row.add_controller(gesture);
    row.set_cursor_from_name(Some("pointer"));

    row.upcast()
}

fn build_result_row_webapp(app: &WebApp, nav: &Arc<NavigationView>) -> Widget {
    let row = GBox::builder()
        .orientation(Orientation::Horizontal)
        .spacing(12)
        .margin_top(8)
        .margin_bottom(8)
        .margin_start(12)
        .margin_end(12)
        .build();

    let icon = load_app_icon(&app.icon_path, &app.icon_url, 48, &app.name);
    row.append(&icon);

    let text = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(2)
        .valign(Align::Center)
        .hexpand(true)
        .build();

    let name_lbl = Label::builder()
        .label(&app.name)
        .halign(Align::Start)
        .css_classes(vec!["heading".to_string()])
        .build();
    let summary_lbl = Label::builder()
        .label(&app.summary)
        .halign(Align::Start)
        .wrap(false)
        .ellipsize(gtk4::pango::EllipsizeMode::End)
        .max_width_chars(50)
        .css_classes(vec!["caption".to_string()])
        .build();
    text.append(&name_lbl);
    text.append(&summary_lbl);
    row.append(&text);

    let badge = Label::builder()
        .label("Web App")
        .css_classes(vec!["caption".to_string(), "dim-label".to_string()])
        .valign(Align::Center)
        .build();
    row.append(&badge);

    let nav = Arc::clone(nav);
    let id = app.id.clone();
    let name = app.name.clone();
    let summary = app.summary.clone();
    let icon_path = app.icon_path.clone();
    let icon_url = app.icon_url.clone();
    let source = app.source.clone();
    let gesture = gtk4::GestureClick::new();
    gesture.connect_released(move |_, _, _, _| {
        super::detail::push_detail(&nav, &id, &name, &summary, &icon_path, &icon_url, &source);
    });
    row.add_controller(gesture);
    row.set_cursor_from_name(Some("pointer"));

    row.upcast()
}
