// pages/explore.rs — Explore page: top-level category grid -> sub-cat tiles + app grid

use gtk4::prelude::*;
use gtk4::{
    glib, Align, Box as GBox, Button, FlowBox, FlowBoxChild, Label, Orientation, ScrolledWindow,
    SelectionMode, Widget,
};
use libadwaita::prelude::*;
use libadwaita::{HeaderBar, NavigationPage, NavigationView, ToolbarView};
use std::cell::RefCell;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use std::sync::{mpsc, Arc};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use rakuos_packages::NativeApp;
use super::icon_helper::load_app_icon;

const PREVIEW_SIZE: usize = 12;
const PAGE_SIZE: usize = 60;

// ── Category tree (mirrors Qt main.qml) ──────────────────────────────────────

struct TopCat {
    label: &'static str,
    cat: &'static str,
    icon: &'static str,
    subs: &'static [(&'static str, &'static str)],
}

const CATEGORIES: &[TopCat] = &[
    TopCat { label: "Games",        cat: "Game",        icon: "applications-games-symbolic", subs: &[
        ("Action",       "ActionGame"),
        ("Adventure",    "AdventureGame"),
        ("Arcade",       "ArcadeGame"),
        ("Board Games",  "BoardGame"),
        ("Card Games",   "CardGame"),
        ("Emulators",    "Emulator"),
        ("Kids",         "KidsGame"),
        ("Logic",        "LogicGame"),
        ("Role Playing", "RolePlaying"),
        ("Shooter",      "Shooter"),
        ("Simulation",   "Simulation"),
        ("Sports",       "SportsGame"),
        ("Strategy",     "StrategyGame"),
    ]},
    TopCat { label: "Internet",     cat: "Network",     icon: "network-wireless-symbolic", subs: &[
        ("Web Browsers",  "WebBrowser"),
        ("Email",         "Email"),
        ("Chat & IM",     "InstantMessaging"),
        ("File Sharing",  "FileTransfer"),
        ("News & RSS",    "News"),
        ("Remote Access", "RemoteAccess"),
    ]},
    TopCat { label: "Audio & Video", cat: "AudioVideo", icon: "audio-headphones-symbolic", subs: &[
        ("Music",   "Music"),
        ("Video",   "Video"),
        ("Editors", "AudioVideoEditing"),
        ("MIDI",    "Midi"),
        ("TV",      "TV"),
        ("Recorders","Recorder"),
        ("Mixers",  "Mixer"),
    ]},
    TopCat { label: "Graphics",     cat: "Graphics",    icon: "applications-graphics-symbolic", subs: &[
        ("2D Editors",  "2DGraphics"),
        ("3D Editors",  "3DGraphics"),
        ("Photography", "Photography"),
        ("Viewers",     "Viewer"),
        ("Publishing",  "Publishing"),
        ("Scanning",    "Scanning"),
    ]},
    TopCat { label: "Productivity", cat: "Office",      icon: "x-office-document-symbolic", subs: &[
        ("Office Suite", "WordProcessor"),
        ("Spreadsheets", "Spreadsheet"),
        ("Presentation", "Presentation"),
        ("Calendar",     "Calendar"),
        ("Finance",      "Finance"),
        ("Contacts",     "ContactManagement"),
        ("Project Mgmt", "ProjectManagement"),
    ]},
    TopCat { label: "Development",  cat: "Development", icon: "applications-development-symbolic", subs: &[
        ("IDEs",            "IDE"),
        ("Version Control", "RevisionControl"),
        ("Debuggers",       "Debugger"),
        ("Web Dev",         "WebDevelopment"),
        ("Database",        "Database"),
        ("Data Viz",        "DataVisualization"),
    ]},
    TopCat { label: "System",       cat: "System",      icon: "preferences-system-symbolic", subs: &[
        ("File Managers", "FileManager"),
        ("Terminal",      "TerminalEmulator"),
        ("Monitors",      "Monitor"),
        ("Security",      "Security"),
        ("Settings",      "Settings"),
        ("Package Mgmt",  "PackageManager"),
    ]},
    TopCat { label: "Utilities",    cat: "Utility",     icon: "applications-utilities-symbolic", subs: &[
        ("Archiving",   "Archiving"),
        ("Calculators", "Calculator"),
        ("Clocks",      "Clock"),
        ("Text Editors","TextEditor"),
        ("File Tools",  "FileTools"),
        ("Dictionary",  "Dictionary"),
    ]},
    TopCat { label: "Science",      cat: "Science",     icon: "applications-science-symbolic", subs: &[
        ("Astronomy",   "Astronomy"),
        ("Chemistry",   "Chemistry"),
        ("Electronics", "Electronics"),
        ("Engineering", "Engineering"),
        ("Geography",   "Geography"),
        ("Physics",     "Physics"),
    ]},
    TopCat { label: "Education",    cat: "Education",   icon: "applications-education-symbolic", subs: &[
        ("Languages",  "Languages"),
        ("Mathematics","Math"),
        ("Science",    "Science"),
        ("Literature", "Literature"),
        ("Geography",  "Geography"),
        ("Kids",       "KidsGame"),
    ]},
];

// ── Main explore page — only top-level category tiles ─────────────────────────

pub fn build(nav: Arc<NavigationView>) -> Widget {
    let scroll = ScrolledWindow::builder()
        .hscrollbar_policy(gtk4::PolicyType::Never)
        .vscrollbar_policy(gtk4::PolicyType::Automatic)
        .vexpand(true)
        .build();

    let main_box = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(16)
        .margin_top(16)
        .margin_bottom(24)
        .margin_start(16)
        .margin_end(16)
        .build();
    scroll.set_child(Some(&main_box));

    let lbl = Label::builder()
        .label("Browse by Category")
        .halign(Align::Start)
        .css_classes(vec!["title-2".to_string()])
        .build();
    main_box.append(&lbl);

    let flow = FlowBox::builder()
        .selection_mode(SelectionMode::None)
        .min_children_per_line(2)
        .max_children_per_line(4)
        .column_spacing(12)
        .row_spacing(12)
        .homogeneous(true)
        .build();

    for top in CATEGORIES {
        let card = build_top_cat_card(top.label, top.cat, top.icon, top.subs, Arc::clone(&nav));
        let child = FlowBoxChild::new();
        child.set_child(Some(&card));
        child.set_focusable(false);
        flow.insert(&child, -1);
    }
    main_box.append(&flow);

    scroll.upcast()
}

// ── Top-level category card ───────────────────────────────────────────────────

fn build_top_cat_card(
    cat_label: &str,
    cat_id: &str,
    icon_name: &str,
    subs: &'static [(&'static str, &'static str)],
    nav: Arc<NavigationView>,
) -> Widget {
    let card = GBox::builder()
        .orientation(Orientation::Horizontal)
        .spacing(12)
        .css_classes(vec!["card".to_string()])
        .margin_top(4)
        .margin_bottom(4)
        .margin_start(4)
        .margin_end(4)
        .build();

    let icon = gtk4::Image::builder()
        .icon_name(icon_name)
        .pixel_size(32)
        .margin_start(12)
        .margin_top(12)
        .margin_bottom(12)
        .build();
    card.append(&icon);

    let name_lbl = Label::builder()
        .label(cat_label)
        .hexpand(true)
        .halign(Align::Start)
        .css_classes(vec!["body".to_string()])
        .build();
    card.append(&name_lbl);

    let chevron = gtk4::Image::builder()
        .icon_name("go-next-symbolic")
        .margin_end(12)
        .build();
    card.append(&chevron);

    let btn = Button::new();
    btn.set_child(Some(&card));
    btn.add_css_class("flat");

    let label_str = cat_label.to_string();
    let id_str    = cat_id.to_string();
    let nav_c = Arc::clone(&nav);

    btn.connect_clicked(move |_| {
        let page_w = build_category_page(&id_str, &label_str, subs, Arc::clone(&nav_c));
        let nav_page = NavigationPage::builder()
            .title(&label_str)
            .child(&page_w)
            .build();
        nav_c.push(&nav_page);
    });

    btn.upcast()
}

// ── Category page: sub-cat grid + top apps flow ───────────────────────────────

fn build_category_page(
    cat_id: &str,
    cat_label: &str,
    subs: &'static [(&'static str, &'static str)],
    nav: Arc<NavigationView>,
) -> Widget {
    let toolbar = ToolbarView::new();
    toolbar.add_top_bar(&HeaderBar::new());

    let scroll = ScrolledWindow::builder()
        .hscrollbar_policy(gtk4::PolicyType::Never)
        .vscrollbar_policy(gtk4::PolicyType::Automatic)
        .vexpand(true)
        .build();

    let main_box = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(16)
        .margin_top(12)
        .margin_bottom(24)
        .margin_start(16)
        .margin_end(16)
        .build();
    scroll.set_child(Some(&main_box));

    // ── Sub-category tile grid ────────────────────────────────────────────────
    if !subs.is_empty() {
        let sub_lbl = Label::builder()
            .label("Sub-categories")
            .halign(Align::Start)
            .css_classes(vec!["title-3".to_string()])
            .build();
        main_box.append(&sub_lbl);

        let sub_flow = FlowBox::builder()
            .selection_mode(SelectionMode::None)
            .min_children_per_line(2)
            .max_children_per_line(3)
            .column_spacing(10)
            .row_spacing(10)
            .homogeneous(true)
            .build();

        for (sub_label, sub_cat) in subs {
            let tile = build_sub_cat_tile(sub_label, sub_cat, Arc::clone(&nav));
            let child = FlowBoxChild::new();
            child.set_child(Some(&tile));
            child.set_focusable(false);
            sub_flow.insert(&child, -1);
        }
        main_box.append(&sub_flow);
    }

    // ── Parent category preview header row ────────────────────────────────────
    let top_header = GBox::builder()
        .orientation(Orientation::Horizontal)
        .spacing(8)
        .build();

    let top_lbl = Label::builder()
        .label(&format!("{} Apps", cat_label))
        .halign(Align::Start)
        .hexpand(true)
        .css_classes(vec!["title-3".to_string()])
        .build();
    top_header.append(&top_lbl);

    let see_all_btn = Button::builder()
        .label("View all apps →")
        .css_classes(vec!["flat".to_string()])
        .visible(false)
        .build();
    top_header.append(&see_all_btn);
    main_box.append(&top_header);

    // ── App flow grid ─────────────────────────────────────────────────────────
    let app_flow = FlowBox::builder()
        .selection_mode(SelectionMode::None)
        .min_children_per_line(3)
        .max_children_per_line(8)
        .column_spacing(12)
        .row_spacing(12)
        .homogeneous(true)
        .build();
    main_box.append(&app_flow);

    let load_more_btn = Button::builder()
        .label("Load More")
        .halign(Align::Center)
        .visible(false)
        .build();
    main_box.append(&load_more_btn);

    // ── Load apps in background ───────────────────────────────────────────────
    let cat = cat_id.to_string();
    let (tx, rx) = mpsc::channel::<Vec<NativeApp>>();
    std::thread::spawn(move || {
        let apps = rakuos_packages::get_by_category(&cat).unwrap_or_default();
        let _ = tx.send(apps);
    });

    // Store all apps for "see all" toggle
    let stored_apps: Rc<RefCell<Vec<NativeApp>>> = Rc::new(RefCell::new(Vec::new()));
    let stored_c = Rc::clone(&stored_apps);
    let nav_c = Arc::clone(&nav);
    let flow_c = app_flow.clone();
    let btn_c = see_all_btn.clone();
    let stored_load = Rc::clone(&stored_apps);

    glib::timeout_add_local(Duration::from_millis(80), move || {
        match rx.try_recv() {
            Ok(apps) => {
                let preview = random_sample(&apps, PREVIEW_SIZE);
                *stored_c.borrow_mut() = apps;
                populate_app_flow(&flow_c, preview.iter(), Arc::clone(&nav_c));
                let total = stored_load.borrow().len();
                if total > PREVIEW_SIZE {
                    btn_c.set_label(&format!("View all {total} apps →"));
                    btn_c.set_visible(true);
                }
                glib::ControlFlow::Break
            }
            Err(mpsc::TryRecvError::Empty) => glib::ControlFlow::Continue,
            Err(_) => glib::ControlFlow::Break,
        }
    });

    // "View all" switches from the random preview to lazy-loaded full browsing.
    let nav_sa = Arc::clone(&nav);
    let flow_sa = app_flow.clone();
    let load_more_sa = load_more_btn.clone();
    let visible_count: Rc<RefCell<usize>> = Rc::new(RefCell::new(0));
    let lazy_enabled: Rc<RefCell<bool>> = Rc::new(RefCell::new(false));
    let visible_sa = Rc::clone(&visible_count);
    let apps_sa = Rc::clone(&stored_apps);
    let lazy_sa = Rc::clone(&lazy_enabled);
    setup_lazy_scroll(
        &scroll,
        &app_flow,
        Rc::clone(&stored_apps),
        Rc::clone(&visible_count),
        Arc::clone(&nav),
        load_more_btn.clone(),
        Rc::clone(&lazy_enabled),
    );
    see_all_btn.connect_clicked(move |btn| {
        *lazy_sa.borrow_mut() = true;
        *visible_sa.borrow_mut() = 0;
        reveal_more_apps(&flow_sa, &apps_sa.borrow(), &visible_sa, Arc::clone(&nav_sa), &load_more_sa);
        btn.set_visible(false);
    });

    let apps_lm = Rc::clone(&stored_apps);
    let visible_lm = Rc::clone(&visible_count);
    let flow_lm = app_flow.clone();
    let nav_lm = Arc::clone(&nav);
    let load_more_lm = load_more_btn.clone();
    load_more_btn.connect_clicked(move |_| {
        reveal_more_apps(&flow_lm, &apps_lm.borrow(), &visible_lm, Arc::clone(&nav_lm), &load_more_lm);
    });

    toolbar.set_content(Some(&scroll));
    toolbar.upcast()
}

// ── Sub-category tile ─────────────────────────────────────────────────────────

fn build_sub_cat_tile(
    sub_label: &str,
    sub_cat: &str,
    nav: Arc<NavigationView>,
) -> Widget {
    let row = GBox::builder()
        .orientation(Orientation::Horizontal)
        .spacing(8)
        .css_classes(vec!["card".to_string()])
        .margin_top(2)
        .margin_bottom(2)
        .margin_start(2)
        .margin_end(2)
        .build();

    let lbl = Label::builder()
        .label(sub_label)
        .hexpand(true)
        .halign(Align::Start)
        .margin_start(12)
        .margin_top(14)
        .margin_bottom(14)
        .css_classes(vec!["body".to_string()])
        .build();
    row.append(&lbl);

    let chevron = gtk4::Image::builder()
        .icon_name("go-next-symbolic")
        .margin_end(10)
        .build();
    row.append(&chevron);

    let btn = Button::new();
    btn.set_child(Some(&row));
    btn.add_css_class("flat");

    let label_str = sub_label.to_string();
    let cat_str   = sub_cat.to_string();
    let nav_c = Arc::clone(&nav);

    btn.connect_clicked(move |_| {
        let page_w = build_sub_page(&cat_str, Arc::clone(&nav_c));
        let nav_page = NavigationPage::builder()
            .title(&label_str)
            .child(&page_w)
            .build();
        nav_c.push(&nav_page);
    });

    btn.upcast()
}

// ── Sub-category page: all apps in a flow grid ────────────────────────────────

fn build_sub_page(cat_id: &str, nav: Arc<NavigationView>) -> Widget {
    let toolbar = ToolbarView::new();
    toolbar.add_top_bar(&HeaderBar::new());

    let scroll = ScrolledWindow::builder()
        .hscrollbar_policy(gtk4::PolicyType::Never)
        .vscrollbar_policy(gtk4::PolicyType::Automatic)
        .vexpand(true)
        .build();

    let main_box = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(12)
        .margin_top(12)
        .margin_bottom(24)
        .margin_start(16)
        .margin_end(16)
        .build();
    scroll.set_child(Some(&main_box));

    let app_flow = FlowBox::builder()
        .selection_mode(SelectionMode::None)
        .min_children_per_line(3)
        .max_children_per_line(8)
        .column_spacing(12)
        .row_spacing(12)
        .homogeneous(true)
        .build();
    main_box.append(&app_flow);

    let load_more_btn = Button::builder()
        .label("Load More")
        .halign(Align::Center)
        .visible(false)
        .build();
    main_box.append(&load_more_btn);

    let cat = cat_id.to_string();
    let (tx, rx) = mpsc::channel::<Vec<NativeApp>>();
    std::thread::spawn(move || {
        let apps = rakuos_packages::get_by_category(&cat).unwrap_or_default();
        let _ = tx.send(apps);
    });

    let nav_c = Arc::clone(&nav);
    let stored_apps: Rc<RefCell<Vec<NativeApp>>> = Rc::new(RefCell::new(Vec::new()));
    let visible_count: Rc<RefCell<usize>> = Rc::new(RefCell::new(0));
    let lazy_enabled: Rc<RefCell<bool>> = Rc::new(RefCell::new(true));
    setup_lazy_scroll(
        &scroll,
        &app_flow,
        Rc::clone(&stored_apps),
        Rc::clone(&visible_count),
        Arc::clone(&nav),
        load_more_btn.clone(),
        lazy_enabled,
    );
    let stored_load = Rc::clone(&stored_apps);
    let visible_load = Rc::clone(&visible_count);
    let flow_load = app_flow.clone();
    let load_more_load = load_more_btn.clone();
    glib::timeout_add_local(Duration::from_millis(80), move || {
        match rx.try_recv() {
            Ok(apps) => {
                if !apps.is_empty() {
                    *stored_load.borrow_mut() = apps;
                    reveal_more_apps(&flow_load, &stored_load.borrow(), &visible_load, Arc::clone(&nav_c), &load_more_load);
                }
                glib::ControlFlow::Break
            }
            Err(mpsc::TryRecvError::Empty) => glib::ControlFlow::Continue,
            Err(_) => glib::ControlFlow::Break,
        }
    });

    let apps_lm = Rc::clone(&stored_apps);
    let visible_lm = Rc::clone(&visible_count);
    let flow_lm = app_flow.clone();
    let nav_lm = Arc::clone(&nav);
    let load_more_lm = load_more_btn.clone();
    load_more_btn.connect_clicked(move |_| {
        reveal_more_apps(&flow_lm, &apps_lm.borrow(), &visible_lm, Arc::clone(&nav_lm), &load_more_lm);
    });

    toolbar.set_content(Some(&scroll));
    toolbar.upcast()
}

// ── Helpers ───────────────────────────────────────────────────────────────────

fn populate_app_flow<'a>(
    flow: &FlowBox,
    apps: impl Iterator<Item = &'a NativeApp>,
    nav: Arc<NavigationView>,
) {
    clear_flow(flow);
    for app in apps {
        append_app_card(flow, app, Arc::clone(&nav));
    }
}

fn clear_flow(flow: &FlowBox) {
    while let Some(child) = flow.first_child() {
        flow.remove(&child);
    }
}

fn append_app_card(flow: &FlowBox, app: &NativeApp, nav: Arc<NavigationView>) {
    let card = build_app_card(app, nav);
    let child = FlowBoxChild::new();
    child.set_child(Some(&card));
    child.set_focusable(false);
    flow.insert(&child, -1);
}

fn reveal_more_apps(
    flow: &FlowBox,
    apps: &[NativeApp],
    visible_count: &Rc<RefCell<usize>>,
    nav: Arc<NavigationView>,
    load_more_btn: &Button,
) {
    let current = *visible_count.borrow();
    let next = (current + PAGE_SIZE).min(apps.len());

    if current == 0 {
        clear_flow(flow);
    }

    for app in apps.iter().take(next).skip(current) {
        append_app_card(flow, app, Arc::clone(&nav));
    }

    *visible_count.borrow_mut() = next;
    load_more_btn.set_visible(next < apps.len());
}

fn setup_lazy_scroll(
    scroll: &ScrolledWindow,
    flow: &FlowBox,
    apps: Rc<RefCell<Vec<NativeApp>>>,
    visible_count: Rc<RefCell<usize>>,
    nav: Arc<NavigationView>,
    load_more_btn: Button,
    lazy_enabled: Rc<RefCell<bool>>,
) {
    let adjustment = scroll.vadjustment();
    let flow = flow.clone();
    adjustment.connect_value_changed(move |adj| {
        if !*lazy_enabled.borrow() {
            return;
        }
        let bottom = adj.value() + adj.page_size();
        if bottom < adj.upper() - 360.0 {
            return;
        }

        let apps_ref = apps.borrow();
        if *visible_count.borrow() >= apps_ref.len() {
            return;
        }

        reveal_more_apps(&flow, &apps_ref, &visible_count, Arc::clone(&nav), &load_more_btn);
    });
}

fn random_sample(apps: &[NativeApp], limit: usize) -> Vec<NativeApp> {
    let seed = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|duration| duration.as_nanos() as u64)
        .unwrap_or_default();
    let mut keyed = apps
        .iter()
        .cloned()
        .map(|app| {
            let mut hasher = DefaultHasher::new();
            app.id.hash(&mut hasher);
            app.name.hash(&mut hasher);
            (hasher.finish() ^ seed, app)
        })
        .collect::<Vec<_>>();
    keyed.sort_by_key(|(key, _)| *key);
    keyed.into_iter().take(limit).map(|(_, app)| app).collect()
}

fn build_app_card(app: &NativeApp, nav: Arc<NavigationView>) -> Widget {
    let card = GBox::builder()
        .orientation(Orientation::Vertical)
        .spacing(6)
        .width_request(120)
        .css_classes(vec!["card".to_string()])
        .margin_top(4)
        .margin_bottom(4)
        .build();

    let icon = load_app_icon(&app.icon_path, &app.icon_url, 56, &app.name);
    icon.set_halign(Align::Center);
    icon.set_margin_top(10);
    card.append(&icon);

    if app.installed {
        let badge = Label::builder()
            .label("✓")
            .css_classes(vec!["caption".to_string()])
            .halign(Align::End)
            .margin_end(8)
            .build();
        card.append(&badge);
    }

    let name_lbl = Label::builder()
        .label(&app.name)
        .halign(Align::Center)
        .wrap(false)
        .ellipsize(gtk4::pango::EllipsizeMode::End)
        .max_width_chars(14)
        .css_classes(vec!["caption-heading".to_string()])
        .margin_start(8)
        .margin_end(8)
        .margin_bottom(10)
        .build();
    card.append(&name_lbl);

    let btn = Button::new();
    btn.set_child(Some(&card));
    btn.add_css_class("flat");

    let (id, name, summary, ip, iu, src) = (
        app.id.clone(), app.name.clone(), app.summary.clone(),
        app.icon_path.clone(), app.icon_url.clone(), app.source.clone(),
    );
    btn.connect_clicked(move |_| {
        super::detail::push_detail(&nav, &id, &name, &summary, &ip, &iu, &src);
    });

    btn.upcast()
}

// build_list_row kept for any external callers
pub fn build_list_row(app: &NativeApp, nav: Arc<NavigationView>) -> Widget {
    build_app_card(app, nav)
}
