use adw::prelude::*;
use crate::icon_theme;
use outlay_core::models::TransactionType;
use outlay_core::db::Database;
use std::rc::Rc;
use std::cell::RefCell;

/// Build a searchable category combo row.
///
/// Returns (combo_row, category_ids) where category_ids maps combo indices to DB IDs.
pub fn make_searchable_category_combo(
    db: &Rc<Database>,
    txn_type: Option<TransactionType>,
    title: &str,
) -> (adw::ComboRow, Rc<RefCell<Vec<i64>>>) {
    let categories = db.list_categories(txn_type).unwrap_or_default();
    let ids: Vec<i64> = categories.iter().map(|c| c.id).collect();
    let entries: Vec<String> = categories.iter().map(|c| {
        let icon = icon_theme::resolve_category_icon(&c.icon, &c.color);
        match icon {
            Some(i) => format!("{}\t{}", i, c.name),
            None => c.name.clone(),
        }
    }).collect();

    let refs: Vec<&str> = entries.iter().map(|s| s.as_str()).collect();
    let model = gtk::StringList::new(&refs);

    let combo = adw::ComboRow::builder()
        .title(title)
        .model(&model)
        .build();

    let factory = make_category_factory();
    combo.set_factory(Some(&factory));
    combo.set_list_factory(Some(&make_category_factory()));

    combo.set_enable_search(true);
    let expression = gtk::PropertyExpression::new(
        gtk::StringObject::static_type(),
        gtk::Expression::NONE,
        "string",
    );
    combo.set_expression(Some(&expression));

    let category_ids = Rc::new(RefCell::new(ids));
    (combo, category_ids)
}

/// Update the combo's model for a different transaction type.
pub fn update_category_combo(
    combo: &adw::ComboRow,
    ids: &Rc<RefCell<Vec<i64>>>,
    db: &Rc<Database>,
    txn_type: Option<TransactionType>,
) {
    let categories = db.list_categories(txn_type).unwrap_or_default();
    let new_ids: Vec<i64> = categories.iter().map(|c| c.id).collect();
    let entries: Vec<String> = categories.iter().map(|c| {
        let icon = icon_theme::resolve_category_icon(&c.icon, &c.color);
        match icon {
            Some(i) => format!("{}\t{}", i, c.name),
            None => c.name.clone(),
        }
    }).collect();

    let refs: Vec<&str> = entries.iter().map(|s| s.as_str()).collect();
    let model = gtk::StringList::new(&refs);
    combo.set_model(Some(&model));
    combo.set_selected(0);
    *ids.borrow_mut() = new_ids;
}

pub fn make_category_factory() -> gtk::SignalListItemFactory {
    let factory = gtk::SignalListItemFactory::new();
    factory.connect_setup(|_, item| {
        let item = item.downcast_ref::<gtk::ListItem>().unwrap();
        let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 8);
        let icon = gtk::Image::new();
        icon.set_pixel_size(20);
        let label = gtk::Label::new(None);
        hbox.append(&icon);
        hbox.append(&label);
        item.set_child(Some(&hbox));
    });
    factory.connect_bind(|_, item| {
        let item = item.downcast_ref::<gtk::ListItem>().unwrap();
        let string_obj = item.item().and_downcast::<gtk::StringObject>().unwrap();
        let text = string_obj.string();
        let hbox = item.child().and_downcast::<gtk::Box>().unwrap();
        let icon = hbox.first_child().and_downcast::<gtk::Image>().unwrap();
        let label = icon.next_sibling().and_downcast::<gtk::Label>().unwrap();
        if let Some((icon_name, name)) = text.split_once('\t') {
            icon.set_icon_name(Some(icon_name));
            icon.set_visible(true);
            label.set_label(name);
        } else {
            icon.set_visible(false);
            label.set_label(&text);
        }
    });
    factory
}
