use adw::prelude::*;
use chrono::{Datelike, NaiveDate};
use outlay_core::db::Database;
use outlay_core::exchange::ExchangeRateService;
use outlay_core::models::{
    Frequency, NewRecurringTransaction, RecurringTransaction, TransactionType,
};
use std::cell::RefCell;
use std::rc::Rc;

use crate::icon_theme;

type ChangeCb = Rc<RefCell<Option<Box<dyn Fn()>>>>;

pub struct RecurringView {
    pub container: gtk::Box,
    db: Rc<Database>,
    active_group: adw::PreferencesGroup,
    paused_group: adw::PreferencesGroup,
    toast_overlay: adw::ToastOverlay,
    on_change: ChangeCb,
}

impl RecurringView {
    pub fn new(db: Rc<Database>) -> Self {
        let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let toast_overlay = adw::ToastOverlay::new();

        let clamp = adw::Clamp::new();
        clamp.set_maximum_size(700);
        clamp.set_margin_start(16);
        clamp.set_margin_end(16);

        let inner = gtk::Box::new(gtk::Orientation::Vertical, 20);
        inner.set_margin_top(20);
        inner.set_margin_bottom(20);

        // Active section
        let active_group = adw::PreferencesGroup::builder()
            .title("ACTIVE")
            .build();

        // Paused section
        let paused_group = adw::PreferencesGroup::builder()
            .title("PAUSED")
            .build();

        let on_change: ChangeCb = Rc::new(RefCell::new(None));

        // Add button
        let add_btn = gtk::Button::with_label("Add Recurring");
        add_btn.add_css_class("suggested-action");
        add_btn.add_css_class("pill");
        add_btn.set_halign(gtk::Align::Center);
        add_btn.set_margin_top(8);

        Self::load_recurring(&db, &active_group, &paused_group, &toast_overlay, &on_change);

        // Wire add button
        {
            let db_ref = db.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let toast_ref = toast_overlay.clone();
            let change_ref = on_change.clone();
            add_btn.connect_clicked(move |btn| {
                Self::show_add_dialog(btn, &db_ref, &active_ref, &paused_ref, &toast_ref, &change_ref);
            });
        }

        inner.append(&active_group);
        inner.append(&paused_group);
        inner.append(&add_btn);

        clamp.set_child(Some(&inner));

        let scroll = gtk::ScrolledWindow::builder()
            .hscrollbar_policy(gtk::PolicyType::Never)
            .vexpand(true)
            .child(&clamp)
            .build();

        toast_overlay.set_child(Some(&scroll));
        container.append(&toast_overlay);

        RecurringView { container, db, active_group, paused_group, toast_overlay, on_change }
    }

    pub fn refresh(&self) {
        Self::load_recurring(&self.db, &self.active_group, &self.paused_group, &self.toast_overlay, &self.on_change);
    }

    pub fn set_on_change(&self, cb: impl Fn() + 'static) {
        *self.on_change.borrow_mut() = Some(Box::new(cb));
    }

    fn notify_change(on_change: &ChangeCb) {
        if let Some(cb) = on_change.borrow().as_ref() {
            cb();
        }
    }

    fn load_recurring(
        db: &Rc<Database>,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        Self::clear_group(active_group);
        Self::clear_group(paused_group);

        let all = db.list_recurring(false).unwrap_or_default();
        let active: Vec<&RecurringTransaction> = all.iter().filter(|r| r.active).collect();
        let paused: Vec<&RecurringTransaction> = all.iter().filter(|r| !r.active).collect();

        if active.is_empty() {
            let row = adw::ActionRow::builder()
                .title("No active recurring transactions")
                .build();
            row.add_css_class("dim-label");
            active_group.add(&row);
        } else {
            for rec in &active {
                let row = Self::make_row(db, rec, active_group, paused_group, toast_overlay, on_change);
                active_group.add(&row);
            }
        }

        if paused.is_empty() {
            let row = adw::ActionRow::builder()
                .title("No paused recurring transactions")
                .build();
            row.add_css_class("dim-label");
            paused_group.add(&row);
        } else {
            for rec in &paused {
                let row = Self::make_row(db, rec, active_group, paused_group, toast_overlay, on_change);
                paused_group.add(&row);
            }
        }
    }

    fn make_row(
        db: &Rc<Database>,
        rec: &RecurringTransaction,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) -> adw::ActionRow {
        let cat = db.get_category(rec.category_id).ok();
        let cat_name = cat.as_ref()
            .map(|c| c.name.clone())
            .unwrap_or_else(|| "Unknown".to_string());
        let cat_icon = cat.as_ref().and_then(|c| c.icon.clone());
        let cat_color = cat.as_ref().and_then(|c| c.color.clone());

        let freq_label = Self::frequency_label(rec.frequency);

        let (title, subtitle) = if rec.subscription_id.is_some() {
            let name = rec.note.as_deref().unwrap_or(&cat_name);
            (name.to_string(), freq_label.to_string())
        } else {
            (cat_name.clone(), format!("{} - {}", rec.currency, freq_label))
        };

        let row = adw::ActionRow::builder()
            .title(&title)
            .subtitle(&subtitle)
            .activatable(true)
            .build();

        let icon_name = icon_theme::resolve_category_icon(&cat_icon, &cat_color);
        if let Some(name) = &icon_name {
            let icon = gtk::Image::from_icon_name(name);
            icon.set_pixel_size(24);
            row.add_prefix(&icon);
        }

        // Amount with styling
        let amount_str = match rec.transaction_type {
            TransactionType::Expense => format!("-{:.2}", rec.amount),
            TransactionType::Income => format!("+{:.2}", rec.amount),
        };
        let amount_label = gtk::Label::new(Some(&amount_str));
        amount_label.add_css_class("amount-display");
        match rec.transaction_type {
            TransactionType::Expense => amount_label.add_css_class("amount-expense"),
            TransactionType::Income => amount_label.add_css_class("amount-income"),
        }
        row.add_suffix(&amount_label);

        if rec.is_bill {
            let bill_icon = gtk::Image::from_icon_name("outlay-recurring");
            bill_icon.set_pixel_size(16);
            bill_icon.set_tooltip_text(Some("Bill"));
            bill_icon.add_css_class("dim-label");
            row.add_suffix(&bill_icon);
        }

        if !rec.active {
            row.add_css_class("dim-label");
        }

        // Next occurrence indicator
        let next_text = if !rec.active {
            if let Some(resume) = rec.resume_date {
                format!("Paused until {}", resume.format("%b %d, %Y"))
            } else {
                "Paused".to_string()
            }
        } else {
            match Self::next_occurrence(rec) {
                Some(date) => format!("Next: {}", date.format("%b %d, %Y")),
                None => {
                    if rec.end_date.map_or(false, |e| e < chrono::Local::now().date_naive()) {
                        "Ended".to_string()
                    } else {
                        format!("from {}", rec.start_date.format("%b %d, %Y"))
                    }
                }
            }
        };
        let next_label = gtk::Label::new(Some(&next_text));
        next_label.add_css_class("dim-label");
        next_label.add_css_class("caption");
        row.add_suffix(&next_label);

        let rec_id = rec.id;
        let db_ref = db.clone();
        let active_ref = active_group.clone();
        let paused_ref = paused_group.clone();
        let toast_ref = toast_overlay.clone();
        let change_ref = on_change.clone();
        row.connect_activated(move |row| {
            Self::show_edit_dialog(row, rec_id, &db_ref, &active_ref, &paused_ref, &toast_ref, &change_ref);
        });

        row
    }

    fn next_occurrence(rec: &RecurringTransaction) -> Option<NaiveDate> {
        if !rec.active {
            return None;
        }
        let from = rec.last_generated.unwrap_or(rec.start_date);
        let next = match rec.frequency {
            Frequency::Daily => from + chrono::Duration::days(1),
            Frequency::Weekly => from + chrono::Duration::days(7),
            Frequency::Biweekly => from + chrono::Duration::days(14),
            Frequency::Monthly => outlay_core::recurring::add_months(from, 1),
            Frequency::Yearly => outlay_core::recurring::add_months(from, 12),
        };
        if let Some(end) = rec.end_date {
            if next > end {
                return None;
            }
        }
        Some(next)
    }

    fn show_add_dialog(
        parent: &gtk::Button,
        db: &Rc<Database>,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        let dialog = adw::Dialog::builder()
            .title("Add Recurring Transaction")
            .content_width(400)
            .content_height(500)
            .build();

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

        // Wizard stack with two steps
        let wizard = gtk::Stack::new();
        wizard.set_transition_type(gtk::StackTransitionType::SlideLeftRight);

        // --- Step 1: Essentials ---
        let step1 = gtk::Box::new(gtk::Orientation::Vertical, 16);
        step1.set_margin_top(16);
        step1.set_margin_bottom(16);
        step1.set_margin_start(16);
        step1.set_margin_end(16);

        // Type toggle
        let type_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        type_box.add_css_class("linked");
        type_box.set_halign(gtk::Align::Center);

        let expense_btn = gtk::ToggleButton::with_label("Expense");
        expense_btn.set_active(true);
        expense_btn.set_hexpand(true);

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

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

        let form1 = adw::PreferencesGroup::new();

        // Amount
        let amount_row = adw::EntryRow::builder()
            .title("Amount")
            .build();
        amount_row.set_input_purpose(gtk::InputPurpose::Number);
        crate::numpad::attach_numpad(&amount_row);
        form1.add(&amount_row);

        // Category
        let category_model = gtk::StringList::new(&[]);
        let category_ids: Rc<RefCell<Vec<i64>>> = Rc::new(RefCell::new(Vec::new()));
        let category_row = adw::ComboRow::builder()
            .title("Category")
            .model(&category_model)
            .build();
        category_row.set_factory(Some(&Self::make_category_factory()));
        category_row.set_list_factory(Some(&Self::make_category_factory()));
        Self::populate_categories(db, &category_model, &category_ids, TransactionType::Expense);
        form1.add(&category_row);

        // Frequency
        let freq_labels = ["Daily", "Weekly", "Biweekly", "Monthly", "Yearly"];
        let freq_model = gtk::StringList::new(&freq_labels);
        let freq_row = adw::ComboRow::builder()
            .title("Frequency")
            .model(&freq_model)
            .selected(3) // Monthly default
            .build();
        form1.add(&freq_row);

        // Wire type toggle to filter categories
        {
            let db_ref = db.clone();
            let model_ref = category_model.clone();
            let ids_ref = category_ids.clone();
            expense_btn.connect_toggled(move |btn| {
                if btn.is_active() {
                    Self::populate_categories(
                        &db_ref, &model_ref, &ids_ref, TransactionType::Expense,
                    );
                }
            });
        }
        {
            let db_ref = db.clone();
            let model_ref = category_model.clone();
            let ids_ref = category_ids.clone();
            income_btn.connect_toggled(move |btn| {
                if btn.is_active() {
                    Self::populate_categories(
                        &db_ref, &model_ref, &ids_ref, TransactionType::Income,
                    );
                }
            });
        }

        let step_label = gtk::Label::new(Some("Step 1 of 2"));
        step_label.add_css_class("caption");
        step_label.add_css_class("dim-label");

        let next_btn = gtk::Button::with_label("Next");
        next_btn.add_css_class("suggested-action");
        next_btn.add_css_class("pill");
        next_btn.set_halign(gtk::Align::Center);

        step1.append(&type_box);
        step1.append(&form1);
        step1.append(&step_label);
        step1.append(&next_btn);

        // --- Step 2: Details ---
        let step2 = gtk::Box::new(gtk::Orientation::Vertical, 16);
        step2.set_margin_top(16);
        step2.set_margin_bottom(16);
        step2.set_margin_start(16);
        step2.set_margin_end(16);

        let form2 = adw::PreferencesGroup::new();

        // Currency
        let base_currency = db
            .get_setting("base_currency")
            .ok()
            .flatten()
            .unwrap_or_else(|| "USD".to_string());
        let mut currencies = ExchangeRateService::supported_currencies();
        if let Some(pos) = currencies.iter().position(|(c, _)| c.eq_ignore_ascii_case(&base_currency)) {
            let base = currencies.remove(pos);
            currencies.insert(0, base);
        }
        let currency_labels: Vec<String> = currencies
            .iter()
            .map(|(code, name)| format!("{} - {}", code, name))
            .collect();
        let currency_label_refs: Vec<&str> = currency_labels.iter().map(|s| s.as_str()).collect();
        let currency_model = gtk::StringList::new(&currency_label_refs);
        let currency_codes: Vec<String> = currencies.iter().map(|(c, _)| c.to_string()).collect();
        let currency_row = adw::ComboRow::builder()
            .title("Currency")
            .model(&currency_model)
            .selected(0u32)
            .build();
        form2.add(&currency_row);

        // Start date (calendar picker)
        let today_str = chrono::Local::now().date_naive().format("%Y-%m-%d").to_string();
        let (start_row, start_date_label) = crate::date_picker::make_date_row("Start Date", &today_str);
        form2.add(&start_row);

        // End date (optional, calendar picker)
        let (end_row, end_date_label) = crate::date_picker::make_date_row("End Date (optional)", "");
        form2.add(&end_row);

        // Note
        let note_row = adw::EntryRow::builder()
            .title("Note (optional)")
            .build();
        form2.add(&note_row);

        // Service picker (shown when category is "Subscriptions")
        let service_model = gtk::StringList::new(&[]);
        let service_ids: Rc<RefCell<Vec<i64>>> = Rc::new(RefCell::new(Vec::new()));
        if let Ok(cats) = db.list_subscription_categories() {
            for cat in &cats {
                let icon_name = icon_theme::resolve_category_icon(&cat.icon, &cat.color);
                let entry = match &icon_name {
                    Some(icon) => format!("{}\t{}", icon, cat.name),
                    None => cat.name.clone(),
                };
                service_model.append(&entry);
                service_ids.borrow_mut().push(cat.id);
            }
        }
        let service_row = adw::ComboRow::builder()
            .title("Service")
            .model(&service_model)
            .build();
        service_row.set_factory(Some(&Self::make_category_factory()));
        service_row.set_list_factory(Some(&Self::make_category_factory()));
        service_row.set_visible(false);
        form2.add(&service_row);

        // Bill toggle
        let bill_row = adw::SwitchRow::builder()
            .title("This is a bill")
            .subtitle("Enable reminders before due date")
            .build();
        form2.add(&bill_row);

        let reminder_row = adw::SpinRow::builder()
            .title("Remind days before")
            .adjustment(&gtk::Adjustment::new(3.0, 1.0, 14.0, 1.0, 1.0, 0.0))
            .build();
        reminder_row.set_visible(false);
        form2.add(&reminder_row);

        {
            let reminder_ref = reminder_row.clone();
            bill_row.connect_active_notify(move |row| {
                reminder_ref.set_visible(row.is_active());
            });
        }

        let step2_label = gtk::Label::new(Some("Step 2 of 2"));
        step2_label.add_css_class("caption");
        step2_label.add_css_class("dim-label");

        let btn_box = gtk::Box::new(gtk::Orientation::Horizontal, 12);
        btn_box.set_halign(gtk::Align::Center);

        let back_btn = gtk::Button::with_label("Back");
        back_btn.add_css_class("pill");

        let save_btn = gtk::Button::with_label("Save");
        save_btn.add_css_class("suggested-action");
        save_btn.add_css_class("pill");

        btn_box.append(&back_btn);
        btn_box.append(&save_btn);

        step2.append(&form2);
        step2.append(&step2_label);
        step2.append(&btn_box);

        // Wire wizard navigation
        {
            let w = wizard.clone();
            let amount_ref = amount_row.clone();
            let ids_ref = category_ids.clone();
            let cat_ref = category_row.clone();
            let toast_ref = toast_overlay.clone();
            let service_ref = service_row.clone();
            let model_ref = category_model.clone();
            next_btn.connect_clicked(move |_| {
                // Validate step 1 before advancing
                let _: f64 = match amount_ref.text().trim().parse() {
                    Ok(v) if v > 0.0 => v,
                    _ => {
                        toast_ref.add_toast(adw::Toast::new("Please enter a valid amount"));
                        return;
                    }
                };
                let cat_idx = cat_ref.selected() as usize;
                if ids_ref.borrow().get(cat_idx).is_none() {
                    toast_ref.add_toast(adw::Toast::new("Please select a category"));
                    return;
                }
                // Show service picker if category is "Subscriptions"
                let cat_name = model_ref
                    .string(cat_idx as u32)
                    .map(|s| {
                        let text = s.to_string();
                        text.split('\t').last().unwrap_or(&text).to_string()
                    })
                    .unwrap_or_default();
                service_ref.set_visible(cat_name == "Subscriptions");
                w.set_visible_child_name("step2");
            });
        }
        {
            let w = wizard.clone();
            back_btn.connect_clicked(move |_| {
                w.set_visible_child_name("step1");
            });
        }

        wizard.add_named(&step1, Some("step1"));
        wizard.add_named(&step2, Some("step2"));

        let scroll = gtk::ScrolledWindow::builder()
            .hscrollbar_policy(gtk::PolicyType::Never)
            .vexpand(true)
            .child(&wizard)
            .build();

        toolbar.set_content(Some(&scroll));
        dialog.set_child(Some(&toolbar));

        // Wire save
        {
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let toast_ref = toast_overlay.clone();
            let ids_ref = category_ids.clone();
            let currency_codes_ref = currency_codes.clone();
            let service_ref = service_row.clone();
            let service_ids_ref = service_ids.clone();
            let change_ref = on_change.clone();
            save_btn.connect_clicked(move |_| {
                let amount: f64 = match amount_row.text().trim().parse() {
                    Ok(v) if v > 0.0 => v,
                    _ => {
                        let toast = adw::Toast::new("Please enter a valid amount");
                        toast_ref.add_toast(toast);
                        return;
                    }
                };

                let txn_type = if expense_btn.is_active() {
                    TransactionType::Expense
                } else {
                    TransactionType::Income
                };

                let cat_idx = category_row.selected() as usize;
                let ids = ids_ref.borrow();
                let category_id = match ids.get(cat_idx) {
                    Some(&id) => id,
                    None => {
                        let toast = adw::Toast::new("Please select a category");
                        toast_ref.add_toast(toast);
                        return;
                    }
                };

                let curr_idx = currency_row.selected() as usize;
                let currency = currency_codes_ref
                    .get(curr_idx)
                    .cloned()
                    .unwrap_or_else(|| "USD".to_string());

                let freq = match freq_row.selected() {
                    0 => Frequency::Daily,
                    1 => Frequency::Weekly,
                    2 => Frequency::Biweekly,
                    3 => Frequency::Monthly,
                    _ => Frequency::Yearly,
                };

                let start_text = start_date_label.label();
                let start_date = match NaiveDate::parse_from_str(start_text.trim(), "%Y-%m-%d") {
                    Ok(d) => d,
                    Err(_) => {
                        let toast = adw::Toast::new("Please select a start date");
                        toast_ref.add_toast(toast);
                        return;
                    }
                };

                let end_text = end_date_label.label();
                let end_date = if end_text.trim().is_empty() {
                    None
                } else {
                    match NaiveDate::parse_from_str(end_text.trim(), "%Y-%m-%d") {
                        Ok(d) => Some(d),
                        Err(_) => None,
                    }
                };

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

                let new_rec = NewRecurringTransaction {
                    amount,
                    transaction_type: txn_type,
                    category_id,
                    currency,
                    note,
                    frequency: freq,
                    start_date,
                    end_date,
                    is_bill: bill_row.is_active(),
                    reminder_days: reminder_row.value() as i32,
                    subscription_id: None,
                };

                let result = if service_ref.is_visible() {
                    let svc_idx = service_ref.selected() as usize;
                    let svc_ids = service_ids_ref.borrow();
                    match svc_ids.get(svc_idx) {
                        Some(&sub_cat_id) => {
                            let svc_name = new_rec.note.as_deref().unwrap_or("Subscription");
                            db_ref.insert_linked_recurring_and_subscription(
                                &new_rec, sub_cat_id, svc_name,
                            )
                        }
                        None => db_ref.insert_recurring(&new_rec),
                    }
                } else {
                    db_ref.insert_recurring(&new_rec)
                };

                match result {
                    Ok(_) => {
                        dialog_ref.close();
                        let toast = adw::Toast::new("Recurring transaction added");
                        toast_ref.add_toast(toast);
                        Self::load_recurring(&db_ref, &active_ref, &paused_ref, &toast_ref, &change_ref);
                        Self::notify_change(&change_ref);
                    }
                    Err(e) => {
                        let toast = adw::Toast::new(&format!("Error: {}", e));
                        toast_ref.add_toast(toast);
                    }
                }
            });
        }

        dialog.present(Some(parent));
    }

    fn show_edit_dialog(
        parent: &adw::ActionRow,
        rec_id: i64,
        db: &Rc<Database>,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        let rec = match db.get_recurring(rec_id) {
            Ok(r) => r,
            Err(_) => return,
        };

        let dialog = adw::Dialog::builder()
            .title("Edit Recurring Transaction")
            .content_width(400)
            .content_height(500)
            .build();

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

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

        let form = adw::PreferencesGroup::new();

        // Amount
        let amount_row = adw::EntryRow::builder()
            .title("Amount")
            .text(&format!("{:.2}", rec.amount))
            .build();
        amount_row.set_input_purpose(gtk::InputPurpose::Number);
        crate::numpad::attach_numpad(&amount_row);
        form.add(&amount_row);

        // Frequency
        let freq_labels = ["Daily", "Weekly", "Biweekly", "Monthly", "Yearly"];
        let freq_model = gtk::StringList::new(&freq_labels);
        let freq_idx = match rec.frequency {
            Frequency::Daily => 0,
            Frequency::Weekly => 1,
            Frequency::Biweekly => 2,
            Frequency::Monthly => 3,
            Frequency::Yearly => 4,
        };
        let freq_row = adw::ComboRow::builder()
            .title("Frequency")
            .model(&freq_model)
            .selected(freq_idx)
            .build();
        form.add(&freq_row);

        // Start date (calendar picker)
        let start_str = rec.start_date.format("%Y-%m-%d").to_string();
        let (start_row, start_date_label) = crate::date_picker::make_date_row("Start Date", &start_str);
        form.add(&start_row);

        // End date (calendar picker)
        let end_str = rec.end_date.map(|d| d.format("%Y-%m-%d").to_string()).unwrap_or_default();
        let (end_row, end_date_label) = crate::date_picker::make_date_row("End Date (optional)", &end_str);
        form.add(&end_row);

        // Note
        let note_row = adw::EntryRow::builder()
            .title("Note (optional)")
            .text(rec.note.as_deref().unwrap_or(""))
            .build();
        form.add(&note_row);

        // Bill toggle
        let bill_row = adw::SwitchRow::builder()
            .title("This is a bill")
            .subtitle("Enable reminders before due date")
            .active(rec.is_bill)
            .build();
        form.add(&bill_row);

        // Reminder days
        let reminder_row = adw::SpinRow::builder()
            .title("Remind days before")
            .adjustment(&gtk::Adjustment::new(
                rec.reminder_days as f64, 1.0, 14.0, 1.0, 1.0, 0.0,
            ))
            .build();
        reminder_row.set_visible(rec.is_bill);
        form.add(&reminder_row);

        {
            let reminder_ref = reminder_row.clone();
            bill_row.connect_active_notify(move |row| {
                reminder_ref.set_visible(row.is_active());
            });
        }

        // Buttons
        let btn_box = gtk::Box::new(gtk::Orientation::Horizontal, 12);
        btn_box.set_halign(gtk::Align::Center);

        let delete_btn = gtk::Button::with_label("Delete");
        delete_btn.add_css_class("destructive-action");
        delete_btn.add_css_class("pill");

        let pause_label = if rec.active { "Pause" } else { "Resume" };
        let pause_btn = gtk::Button::with_label(pause_label);
        pause_btn.add_css_class("pill");

        let save_btn = gtk::Button::with_label("Save");
        save_btn.add_css_class("suggested-action");
        save_btn.add_css_class("pill");

        btn_box.append(&delete_btn);
        btn_box.append(&pause_btn);
        btn_box.append(&save_btn);

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

        // Save
        {
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let toast_ref = toast_overlay.clone();
            let rec_clone = rec.clone();
            let bill_row_ref = bill_row.clone();
            let reminder_row_ref = reminder_row.clone();
            let change_ref = on_change.clone();
            save_btn.connect_clicked(move |_| {
                let amount: f64 = match amount_row.text().trim().parse() {
                    Ok(v) if v > 0.0 => v,
                    _ => {
                        let toast = adw::Toast::new("Please enter a valid amount");
                        toast_ref.add_toast(toast);
                        return;
                    }
                };

                let freq = match freq_row.selected() {
                    0 => Frequency::Daily,
                    1 => Frequency::Weekly,
                    2 => Frequency::Biweekly,
                    3 => Frequency::Monthly,
                    _ => Frequency::Yearly,
                };

                let start_text = start_date_label.label();
                let start_date = match NaiveDate::parse_from_str(start_text.trim(), "%Y-%m-%d") {
                    Ok(d) => d,
                    Err(_) => {
                        let toast = adw::Toast::new("Please select a start date");
                        toast_ref.add_toast(toast);
                        return;
                    }
                };

                let end_text_val = end_date_label.label();
                let end_date = if end_text_val.trim().is_empty() {
                    None
                } else {
                    match NaiveDate::parse_from_str(end_text_val.trim(), "%Y-%m-%d") {
                        Ok(d) => Some(d),
                        Err(_) => None,
                    }
                };

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

                let updated = RecurringTransaction {
                    id: rec_clone.id,
                    amount,
                    transaction_type: rec_clone.transaction_type,
                    category_id: rec_clone.category_id,
                    currency: rec_clone.currency.clone(),
                    note,
                    frequency: freq,
                    start_date,
                    end_date,
                    last_generated: rec_clone.last_generated,
                    active: rec_clone.active,
                    resume_date: rec_clone.resume_date,
                    is_bill: bill_row_ref.is_active(),
                    reminder_days: reminder_row_ref.value() as i32,
                    subscription_id: rec_clone.subscription_id,
                };

                match db_ref.update_recurring(&updated) {
                    Ok(()) => {
                        dialog_ref.close();
                        let toast = adw::Toast::new("Recurring transaction updated");
                        toast_ref.add_toast(toast);
                        Self::load_recurring(&db_ref, &active_ref, &paused_ref, &toast_ref, &change_ref);
                        Self::notify_change(&change_ref);
                    }
                    Err(e) => {
                        let toast = adw::Toast::new(&format!("Error: {}", e));
                        toast_ref.add_toast(toast);
                    }
                }
            });
        }

        // Pause/Resume (5.3: pause with optional resume date)
        {
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let toast_ref = toast_overlay.clone();
            let is_active = rec.active;
            let change_ref = on_change.clone();
            pause_btn.connect_clicked(move |btn| {
                if !is_active {
                    // Resume immediately
                    match db_ref.toggle_recurring_active(rec_id, true) {
                        Ok(()) => {
                            dialog_ref.close();
                            let toast = adw::Toast::new("Recurring transaction resumed");
                            toast_ref.add_toast(toast);
                            Self::load_recurring(&db_ref, &active_ref, &paused_ref, &toast_ref, &change_ref);
                            Self::notify_change(&change_ref);
                        }
                        Err(e) => {
                            let toast = adw::Toast::new(&format!("Error: {}", e));
                            toast_ref.add_toast(toast);
                        }
                    }
                    return;
                }

                // Show pause options dialog
                let pause_dialog = adw::AlertDialog::new(
                    Some("Pause recurring transaction"),
                    Some("Choose when to automatically resume, or pause until you manually resume."),
                );
                pause_dialog.add_response("cancel", "Cancel");
                pause_dialog.add_response("indefinite", "Pause indefinitely");
                pause_dialog.add_response("until", "Pause until date...");
                pause_dialog.set_default_response(Some("indefinite"));
                pause_dialog.set_close_response("cancel");

                let db_pause = db_ref.clone();
                let dialog_close = dialog_ref.clone();
                let active_p = active_ref.clone();
                let paused_p = paused_ref.clone();
                let toast_p = toast_ref.clone();
                let change_p = change_ref.clone();
                pause_dialog.connect_response(None, move |_alert, response| {
                    match response {
                        "indefinite" => {
                            match db_pause.pause_recurring_with_resume(rec_id, None) {
                                Ok(()) => {
                                    dialog_close.close();
                                    let toast = adw::Toast::new("Recurring transaction paused");
                                    toast_p.add_toast(toast);
                                    Self::load_recurring(&db_pause, &active_p, &paused_p, &toast_p, &change_p);
                                    Self::notify_change(&change_p);
                                }
                                Err(e) => {
                                    let toast = adw::Toast::new(&format!("Error: {}", e));
                                    toast_p.add_toast(toast);
                                }
                            }
                        }
                        "until" => {
                            // Show a date picker dialog
                            let date_dialog = adw::Dialog::builder()
                                .title("Resume Date")
                                .content_width(320)
                                .content_height(380)
                                .build();
                            let toolbar = adw::ToolbarView::new();
                            toolbar.add_top_bar(&adw::HeaderBar::new());

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

                            let hint = gtk::Label::new(Some("Select a date to automatically resume:"));
                            hint.add_css_class("dim-label");
                            hint.set_halign(gtk::Align::Start);
                            date_content.append(&hint);

                            let calendar = gtk::Calendar::new();
                            // Set to tomorrow
                            let tomorrow = chrono::Local::now().date_naive() + chrono::Duration::days(1);
                            if let Ok(dt) = gtk::glib::DateTime::from_local(
                                tomorrow.year() as i32,
                                tomorrow.month() as i32,
                                tomorrow.day() as i32,
                                0, 0, 0.0,
                            ) {
                                calendar.set_year(dt.year());
                                calendar.set_month(dt.month() - 1);
                                calendar.set_day(dt.day_of_month());
                            }
                            date_content.append(&calendar);

                            let confirm_btn = gtk::Button::with_label("Pause until this date");
                            confirm_btn.add_css_class("suggested-action");
                            confirm_btn.add_css_class("pill");
                            confirm_btn.set_halign(gtk::Align::Center);
                            date_content.append(&confirm_btn);

                            toolbar.set_content(Some(&date_content));
                            date_dialog.set_child(Some(&toolbar));

                            let db_d = db_pause.clone();
                            let dialog_d = dialog_close.clone();
                            let date_dialog_d = date_dialog.clone();
                            let active_d = active_p.clone();
                            let paused_d = paused_p.clone();
                            let toast_d = toast_p.clone();
                            let change_d = change_p.clone();
                            confirm_btn.connect_clicked(move |_| {
                                let dt = calendar.date();
                                let y = dt.year();
                                let m = dt.month();
                                let d = dt.day_of_month();
                                if let Some(resume) = chrono::NaiveDate::from_ymd_opt(y, m as u32, d as u32) {
                                    match db_d.pause_recurring_with_resume(rec_id, Some(resume)) {
                                        Ok(()) => {
                                            date_dialog_d.close();
                                            dialog_d.close();
                                            let msg = format!("Paused until {}", resume.format("%b %d, %Y"));
                                            let toast = adw::Toast::new(&msg);
                                            toast_d.add_toast(toast);
                                            Self::load_recurring(&db_d, &active_d, &paused_d, &toast_d, &change_d);
                                            Self::notify_change(&change_d);
                                        }
                                        Err(e) => {
                                            let toast = adw::Toast::new(&format!("Error: {}", e));
                                            toast_d.add_toast(toast);
                                        }
                                    }
                                }
                            });

                            date_dialog.present(Some(&dialog_close));
                        }
                        _ => {}
                    }
                });
                pause_dialog.present(Some(btn));
            });
        }

        // Delete
        {
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let toast_ref = toast_overlay.clone();
            let change_ref = on_change.clone();
            delete_btn.connect_clicked(move |btn| {
                let alert = adw::AlertDialog::new(
                    Some("Delete this recurring transaction?"),
                    Some("This will not remove previously generated transactions."),
                );
                alert.add_response("cancel", "Cancel");
                alert.add_response("delete", "Delete");
                alert.set_response_appearance("delete", adw::ResponseAppearance::Destructive);
                alert.set_default_response(Some("cancel"));
                alert.set_close_response("cancel");

                let db_del = db_ref.clone();
                let dialog_del = dialog_ref.clone();
                let active_del = active_ref.clone();
                let paused_del = paused_ref.clone();
                let toast_del = toast_ref.clone();
                let change_del = change_ref.clone();
                alert.connect_response(None, move |_, response| {
                    if response == "delete" {
                        match db_del.delete_recurring_with_cascade(rec_id) {
                            Ok(()) => {
                                dialog_del.close();
                                let toast = adw::Toast::new("Recurring transaction deleted");
                                toast_del.add_toast(toast);
                                Self::load_recurring(
                                    &db_del, &active_del, &paused_del, &toast_del, &change_del,
                                );
                                Self::notify_change(&change_del);
                            }
                            Err(e) => {
                                let toast = adw::Toast::new(&format!("Error: {}", e));
                                toast_del.add_toast(toast);
                            }
                        }
                    }
                });

                alert.present(Some(btn));
            });
        }

        dialog.present(Some(parent));
    }

    fn populate_categories(
        db: &Database,
        model: &gtk::StringList,
        ids: &Rc<RefCell<Vec<i64>>>,
        txn_type: TransactionType,
    ) {
        while model.n_items() > 0 {
            model.remove(0);
        }
        let mut id_list = ids.borrow_mut();
        id_list.clear();

        if let Ok(cats) = db.list_categories(Some(txn_type)) {
            for cat in cats {
                let icon_name = icon_theme::resolve_category_icon(&cat.icon, &cat.color);
                let entry = match &icon_name {
                    Some(icon) => format!("{}\t{}", icon, cat.name),
                    None => cat.name.clone(),
                };
                model.append(&entry);
                id_list.push(cat.id);
            }
        }
    }

    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
    }

    fn clear_group(group: &adw::PreferencesGroup) {
        let mut rows = Vec::new();
        Self::collect_action_rows(group.upcast_ref(), &mut rows);
        for row in &rows {
            group.remove(row);
        }
    }

    fn collect_action_rows(widget: &gtk::Widget, rows: &mut Vec<adw::ActionRow>) {
        if let Some(row) = widget.downcast_ref::<adw::ActionRow>() {
            rows.push(row.clone());
            return;
        }
        let mut child = widget.first_child();
        while let Some(c) = child {
            Self::collect_action_rows(&c, rows);
            child = c.next_sibling();
        }
    }

    fn frequency_label(freq: Frequency) -> &'static str {
        match freq {
            Frequency::Daily => "Daily",
            Frequency::Weekly => "Weekly",
            Frequency::Biweekly => "Biweekly",
            Frequency::Monthly => "Monthly",
            Frequency::Yearly => "Yearly",
        }
    }
}
