use adw::prelude::*;
use gtk::glib;
use outlay_core::db::Database;
use outlay_core::models::{NewTransaction, TransactionType};
use std::cell::Cell;
use std::rc::Rc;

use crate::icon_theme;

pub fn show_quick_add(db: &Rc<Database>, parent_window: Option<&impl IsA<gtk::Widget>>) {
    let base_currency = db
        .get_setting("base_currency")
        .ok()
        .flatten()
        .unwrap_or_else(|| "USD".to_string());

    let dialog = adw::Dialog::builder()
        .title("Quick Add")
        .content_width(380)
        .content_height(420)
        .build();

    let toolbar = adw::ToolbarView::new();
    let header = adw::HeaderBar::new();
    toolbar.add_top_bar(&header);

    let content = gtk::Box::new(gtk::Orientation::Vertical, 12);
    content.set_margin_start(16);
    content.set_margin_end(16);
    content.set_margin_top(12);
    content.set_margin_bottom(16);

    let is_income = Rc::new(Cell::new(false));
    let type_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
    type_box.set_halign(gtk::Align::Center);
    type_box.add_css_class("linked");

    let expense_btn = gtk::ToggleButton::with_label("Expense");
    expense_btn.set_active(true);
    let income_btn = gtk::ToggleButton::with_label("Income");
    income_btn.set_group(Some(&expense_btn));

    type_box.append(&expense_btn);
    type_box.append(&income_btn);
    content.append(&type_box);

    {
        let is_income_ref = is_income.clone();
        income_btn.connect_toggled(move |btn| {
            is_income_ref.set(btn.is_active());
        });
    }

    let amount_group = adw::PreferencesGroup::new();
    let amount_row = adw::EntryRow::builder()
        .title("Amount")
        .build();
    crate::numpad::attach_numpad(&amount_row);
    amount_group.add(&amount_row);
    content.append(&amount_group);

    let cat_group = adw::PreferencesGroup::new();
    let expense_cats = db
        .list_categories(Some(TransactionType::Expense))
        .unwrap_or_default();
    let income_cats = db
        .list_categories(Some(TransactionType::Income))
        .unwrap_or_default();

    let all_expense_entries: Vec<String> = expense_cats
        .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 all_income_entries: Vec<String> = income_cats
        .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 all_expense_ids: Vec<i64> = expense_cats.iter().map(|c| c.id).collect();
    let all_income_ids: Vec<i64> = income_cats.iter().map(|c| c.id).collect();

    let expense_label_refs: Vec<&str> = all_expense_entries.iter().map(|s| s.as_str()).collect();
    let expense_model = gtk::StringList::new(&expense_label_refs);

    let category_row = adw::ComboRow::builder()
        .title("Category")
        .model(&expense_model)
        .build();
    category_row.set_factory(Some(&make_category_factory()));
    category_row.set_list_factory(Some(&make_category_factory()));
    cat_group.add(&category_row);
    content.append(&cat_group);

    // Update category list on type toggle
    {
        let cat_row = category_row.clone();
        let exp_entries = all_expense_entries.clone();
        let inc_entries = all_income_entries.clone();
        income_btn.connect_toggled(move |btn| {
            let entries = if btn.is_active() {
                &inc_entries
            } else {
                &exp_entries
            };
            let refs: Vec<&str> = entries.iter().map(|s| s.as_str()).collect();
            let model = gtk::StringList::new(&refs);
            cat_row.set_model(Some(&model));
            cat_row.set_selected(0);
        });
    }

    let note_group = adw::PreferencesGroup::new();
    let note_row = adw::EntryRow::builder()
        .title("Note (optional)")
        .build();
    note_group.add(&note_row);
    content.append(&note_group);

    let save_btn = gtk::Button::with_label("Save");
    save_btn.add_css_class("suggested-action");
    save_btn.add_css_class("pill");
    save_btn.set_halign(gtk::Align::Center);
    save_btn.set_margin_top(8);

    {
        let db_ref = db.clone();
        let dialog_ref = dialog.clone();
        let amount_ref = amount_row.clone();
        let note_ref = note_row.clone();
        let category_ref = category_row.clone();
        let is_income_ref = is_income.clone();
        let base_curr = base_currency.clone();
        let exp_ids = all_expense_ids;
        let inc_ids = all_income_ids;

        save_btn.connect_clicked(move |_| {
            let amount_text = amount_ref.text().to_string();
            let amount = outlay_core::expr::eval_expr(&amount_text).unwrap_or(0.0);

            if amount <= 0.0 {
                return;
            }

            let is_inc = is_income_ref.get();
            let txn_type = if is_inc {
                TransactionType::Income
            } else {
                TransactionType::Expense
            };

            let cat_idx = category_ref.selected() as usize;
            let cat_id = if is_inc {
                inc_ids.get(cat_idx).copied().unwrap_or(1)
            } else {
                exp_ids.get(cat_idx).copied().unwrap_or(1)
            };

            let note_text = note_ref.text().to_string();
            let note = if note_text.is_empty() {
                None
            } else {
                Some(note_text)
            };

            let today = chrono::Local::now().date_naive();

            let txn = NewTransaction {
                amount,
                transaction_type: txn_type,
                category_id: cat_id,
                currency: base_curr.clone(),
                exchange_rate: 1.0,
                note,
                date: today,
                recurring_id: None,
                payee: None,
            };

            match db_ref.insert_transaction(&txn) {
                Ok(_) => {
                    let type_str = if is_inc { "Income" } else { "Expense" };
                    let body = format!("{}: {:.2} {}", type_str, amount, base_curr);
                    outlay_core::notifications::send_notification(
                        "Transaction Saved",
                        &body,
                        "normal",
                    );
                    dialog_ref.close();
                }
                Err(e) => {
                    eprintln!("Quick-add save error: {}", e);
                }
            }
        });
    }

    content.append(&save_btn);
    toolbar.set_content(Some(&content));
    dialog.set_child(Some(&toolbar));

    // Focus amount on show
    let amount_focus = amount_row.clone();
    glib::idle_add_local_once(move || {
        amount_focus.grab_focus();
    });

    if let Some(win) = parent_window {
        dialog.present(Some(win.as_ref()));
    } else {
        dialog.present(gtk::Widget::NONE);
    }
}

pub fn show_quick_add_popup(db: &Rc<Database>, app: &adw::Application) {
    let base_currency = db
        .get_setting("base_currency")
        .ok()
        .flatten()
        .unwrap_or_else(|| "USD".to_string());

    let win = adw::Window::builder()
        .title("Quick Add")
        .default_width(380)
        .default_height(460)
        .resizable(false)
        .application(app)
        .build();

    let toolbar = adw::ToolbarView::new();
    let header = adw::HeaderBar::new();
    toolbar.add_top_bar(&header);

    let content = gtk::Box::new(gtk::Orientation::Vertical, 12);
    content.set_margin_start(16);
    content.set_margin_end(16);
    content.set_margin_top(12);
    content.set_margin_bottom(16);

    let is_income = Rc::new(Cell::new(false));
    let type_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
    type_box.set_halign(gtk::Align::Center);
    type_box.add_css_class("linked");

    let expense_btn = gtk::ToggleButton::with_label("Expense");
    expense_btn.set_active(true);
    let income_btn = gtk::ToggleButton::with_label("Income");
    income_btn.set_group(Some(&expense_btn));

    type_box.append(&expense_btn);
    type_box.append(&income_btn);
    content.append(&type_box);

    {
        let is_income_ref = is_income.clone();
        income_btn.connect_toggled(move |btn| {
            is_income_ref.set(btn.is_active());
        });
    }

    let amount_group = adw::PreferencesGroup::new();
    let amount_row = adw::EntryRow::builder()
        .title("Amount")
        .build();
    crate::numpad::attach_numpad(&amount_row);
    amount_group.add(&amount_row);
    content.append(&amount_group);

    let cat_group = adw::PreferencesGroup::new();
    let expense_cats = db
        .list_categories(Some(TransactionType::Expense))
        .unwrap_or_default();
    let income_cats = db
        .list_categories(Some(TransactionType::Income))
        .unwrap_or_default();

    let all_expense_entries: Vec<String> = expense_cats
        .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 all_income_entries: Vec<String> = income_cats
        .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 all_expense_ids: Vec<i64> = expense_cats.iter().map(|c| c.id).collect();
    let all_income_ids: Vec<i64> = income_cats.iter().map(|c| c.id).collect();

    let expense_label_refs: Vec<&str> = all_expense_entries.iter().map(|s| s.as_str()).collect();
    let expense_model = gtk::StringList::new(&expense_label_refs);

    let category_row = adw::ComboRow::builder()
        .title("Category")
        .model(&expense_model)
        .build();
    category_row.set_factory(Some(&make_category_factory()));
    category_row.set_list_factory(Some(&make_category_factory()));
    cat_group.add(&category_row);
    content.append(&cat_group);

    // Update category list on type toggle
    {
        let cat_row = category_row.clone();
        let exp_entries = all_expense_entries.clone();
        let inc_entries = all_income_entries.clone();
        income_btn.connect_toggled(move |btn| {
            let entries = if btn.is_active() {
                &inc_entries
            } else {
                &exp_entries
            };
            let refs: Vec<&str> = entries.iter().map(|s| s.as_str()).collect();
            let model = gtk::StringList::new(&refs);
            cat_row.set_model(Some(&model));
            cat_row.set_selected(0);
        });
    }

    let note_group = adw::PreferencesGroup::new();
    let note_row = adw::EntryRow::builder()
        .title("Note (optional)")
        .build();
    note_group.add(&note_row);
    content.append(&note_group);

    let save_btn = gtk::Button::with_label("Save");
    save_btn.add_css_class("suggested-action");
    save_btn.add_css_class("pill");
    save_btn.set_halign(gtk::Align::Center);
    save_btn.set_margin_top(8);

    {
        let db_ref = db.clone();
        let win_ref = win.clone();
        let amount_ref = amount_row.clone();
        let note_ref = note_row.clone();
        let category_ref = category_row.clone();
        let is_income_ref = is_income.clone();
        let base_curr = base_currency.clone();
        let exp_ids = all_expense_ids;
        let inc_ids = all_income_ids;

        save_btn.connect_clicked(move |_| {
            let amount_text = amount_ref.text().to_string();
            let amount = outlay_core::expr::eval_expr(&amount_text).unwrap_or(0.0);

            if amount <= 0.0 {
                return;
            }

            let is_inc = is_income_ref.get();
            let txn_type = if is_inc {
                TransactionType::Income
            } else {
                TransactionType::Expense
            };

            let cat_idx = category_ref.selected() as usize;
            let cat_id = if is_inc {
                inc_ids.get(cat_idx).copied().unwrap_or(1)
            } else {
                exp_ids.get(cat_idx).copied().unwrap_or(1)
            };

            let note_text = note_ref.text().to_string();
            let note = if note_text.is_empty() {
                None
            } else {
                Some(note_text)
            };

            let today = chrono::Local::now().date_naive();

            let txn = NewTransaction {
                amount,
                transaction_type: txn_type,
                category_id: cat_id,
                currency: base_curr.clone(),
                exchange_rate: 1.0,
                note,
                date: today,
                recurring_id: None,
                payee: None,
            };

            match db_ref.insert_transaction(&txn) {
                Ok(_) => {
                    let type_str = if is_inc { "Income" } else { "Expense" };
                    let body = format!("{}: {:.2} {}", type_str, amount, base_curr);
                    outlay_core::notifications::send_notification(
                        "Transaction Saved",
                        &body,
                        "normal",
                    );
                    win_ref.close();
                }
                Err(e) => {
                    eprintln!("Quick-add save error: {}", e);
                }
            }
        });
    }

    content.append(&save_btn);
    toolbar.set_content(Some(&content));
    win.set_content(Some(&toolbar));

    // Focus amount on show
    let amount_focus = amount_row.clone();
    glib::idle_add_local_once(move || {
        amount_focus.grab_focus();
    });

    win.present();
}

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
}
