use adw::prelude::*;
use chrono::NaiveDate;
use outlay_core::db::Database;
use outlay_core::exchange::ExchangeRateService;
use outlay_core::models::{Frequency, NewSubscription, Subscription};
use std::cell::RefCell;
use std::rc::Rc;

use crate::icon_theme;

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

pub struct SubscriptionsView {
    pub container: gtk::Box,
    db: Rc<Database>,
    active_group: adw::PreferencesGroup,
    paused_group: adw::PreferencesGroup,
    monthly_label: gtk::Label,
    yearly_label: gtk::Label,
    base_currency: String,
    toast_overlay: adw::ToastOverlay,
    on_change: ChangeCb,
}

impl SubscriptionsView {
    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);

        let base_currency = db.get_setting("base_currency")
            .ok().flatten()
            .unwrap_or_else(|| "USD".to_string());

        // Cost summary card
        let cost_card = gtk::Box::new(gtk::Orientation::Vertical, 4);
        cost_card.add_css_class("card");
        cost_card.set_margin_start(4);
        cost_card.set_margin_end(4);

        let cost_title = gtk::Label::new(Some("SUBSCRIPTION COST"));
        cost_title.add_css_class("caption");
        cost_title.add_css_class("dim-label");
        cost_title.set_halign(gtk::Align::Start);
        cost_title.set_margin_top(12);
        cost_title.set_margin_start(12);

        let monthly = db.get_subscription_monthly_total().unwrap_or(0.0);
        let yearly = db.get_subscription_yearly_total().unwrap_or(0.0);

        let monthly_label = gtk::Label::new(Some(&format!("{:.2} {}/month", monthly, base_currency)));
        monthly_label.add_css_class("title-1");
        monthly_label.set_halign(gtk::Align::Start);
        monthly_label.set_margin_start(12);

        let yearly_label = gtk::Label::new(Some(&format!("{:.2} {}/year", yearly, base_currency)));
        yearly_label.add_css_class("caption");
        yearly_label.add_css_class("dim-label");
        yearly_label.set_halign(gtk::Align::Start);
        yearly_label.set_margin_start(12);
        yearly_label.set_margin_bottom(12);

        cost_card.append(&cost_title);
        cost_card.append(&monthly_label);
        cost_card.append(&yearly_label);

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

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

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

        Self::load_subscriptions(
            &db, &active_group, &paused_group,
            &monthly_label, &yearly_label, &base_currency,
            &toast_overlay, &on_change,
        );

        // Add button
        let add_btn = gtk::Button::with_label("Add Subscription");
        add_btn.add_css_class("suggested-action");
        add_btn.add_css_class("pill");
        add_btn.set_halign(gtk::Align::Center);
        {
            let db_ref = db.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.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,
                    &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                    &change_ref,
                );
            });
        }

        inner.append(&cost_card);
        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);

        SubscriptionsView {
            container,
            db,
            active_group,
            paused_group,
            monthly_label,
            yearly_label,
            base_currency,
            toast_overlay,
            on_change,
        }
    }

    pub fn refresh(&self) {
        Self::load_subscriptions(
            &self.db, &self.active_group, &self.paused_group,
            &self.monthly_label, &self.yearly_label, &self.base_currency,
            &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_subscriptions(
        db: &Rc<Database>,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        monthly_label: &gtk::Label,
        yearly_label: &gtk::Label,
        base_currency: &str,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        Self::clear_group(active_group);
        Self::clear_group(paused_group);

        let monthly = db.get_subscription_monthly_total().unwrap_or(0.0);
        let yearly = db.get_subscription_yearly_total().unwrap_or(0.0);
        monthly_label.set_label(&format!("{:.2} {}/month", monthly, base_currency));
        yearly_label.set_label(&format!("{:.2} {}/year", yearly, base_currency));

        let subs = db.list_subscriptions_v2().unwrap_or_default();
        let active_subs: Vec<&Subscription> = subs.iter().filter(|s| s.active).collect();
        let paused_subs: Vec<&Subscription> = subs.iter().filter(|s| !s.active).collect();

        if active_subs.is_empty() {
            let row = adw::ActionRow::builder()
                .title("No active subscriptions")
                .subtitle("Add your first subscription")
                .build();
            row.add_css_class("dim-label");
            active_group.add(&row);
        } else {
            for sub in &active_subs {
                let row = Self::make_subscription_row(
                    db, sub, active_group, paused_group,
                    monthly_label, yearly_label, base_currency, toast_overlay,
                    on_change,
                );
                active_group.add(&row);
            }
        }

        if paused_subs.is_empty() {
            paused_group.set_visible(false);
        } else {
            paused_group.set_visible(true);
            for sub in &paused_subs {
                let row = Self::make_subscription_row(
                    db, sub, active_group, paused_group,
                    monthly_label, yearly_label, base_currency, toast_overlay,
                    on_change,
                );
                paused_group.add(&row);
            }
        }
    }

    fn make_subscription_row(
        db: &Rc<Database>,
        sub: &Subscription,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        monthly_label: &gtk::Label,
        yearly_label: &gtk::Label,
        base_currency: &str,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) -> adw::ActionRow {
        let cat = db.get_subscription_category(sub.category_id).ok();
        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 = match sub.frequency {
            Frequency::Daily => "Daily",
            Frequency::Weekly => "Weekly",
            Frequency::Biweekly => "Biweekly",
            Frequency::Monthly => "Monthly",
            Frequency::Yearly => "Yearly",
        };

        let row = adw::ActionRow::builder()
            .title(&sub.name)
            .subtitle(freq_label)
            .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);
        }

        let suffix_box = gtk::Box::new(gtk::Orientation::Horizontal, 4);
        suffix_box.set_valign(gtk::Align::Center);

        let monthly_equiv = match sub.frequency {
            Frequency::Daily => sub.amount * 30.0,
            Frequency::Weekly => sub.amount * 4.33,
            Frequency::Biweekly => sub.amount * 2.17,
            Frequency::Monthly => sub.amount,
            Frequency::Yearly => sub.amount / 12.0,
        };
        let equiv_label = gtk::Label::new(Some(&format!("{:.2} {}/mo", monthly_equiv, sub.currency)));
        equiv_label.add_css_class("caption");
        equiv_label.add_css_class("dim-label");
        suffix_box.append(&equiv_label);

        // Pause/resume button
        let pause_btn = if sub.active {
            let btn = gtk::Button::from_icon_name("tabler-player-pause");
            btn.set_tooltip_text(Some("Pause"));
            btn
        } else {
            let btn = gtk::Button::from_icon_name("tabler-player-play");
            btn.set_tooltip_text(Some("Resume"));
            btn
        };
        pause_btn.add_css_class("flat");
        {
            let sub_id = sub.id;
            let db_ref = db.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.to_string();
            let toast_ref = toast_overlay.clone();
            let change_ref = on_change.clone();
            pause_btn.connect_clicked(move |_| {
                let _ = db_ref.toggle_subscription_active(sub_id);
                Self::load_subscriptions(
                    &db_ref, &active_ref, &paused_ref,
                    &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                    &change_ref,
                );
                Self::notify_change(&change_ref);
            });
        }
        suffix_box.append(&pause_btn);

        // Edit button
        let edit_btn = gtk::Button::from_icon_name("tabler-edit");
        edit_btn.add_css_class("flat");
        edit_btn.set_tooltip_text(Some("Edit"));
        {
            let sub_clone = sub.clone();
            let db_ref = db.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.to_string();
            let toast_ref = toast_overlay.clone();
            let change_ref = on_change.clone();
            edit_btn.connect_clicked(move |btn| {
                Self::show_edit_dialog(
                    btn, &db_ref, &sub_clone, &active_ref, &paused_ref,
                    &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                    &change_ref,
                );
            });
        }
        suffix_box.append(&edit_btn);

        // Delete button
        let del_btn = gtk::Button::from_icon_name("tabler-trash");
        del_btn.add_css_class("flat");
        del_btn.set_tooltip_text(Some("Delete"));
        {
            let sub_id = sub.id;
            let db_ref = db.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.to_string();
            let toast_ref = toast_overlay.clone();
            let change_ref = on_change.clone();
            del_btn.connect_clicked(move |_| {
                let _ = db_ref.delete_subscription_with_cascade(sub_id);
                Self::load_subscriptions(
                    &db_ref, &active_ref, &paused_ref,
                    &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                    &change_ref,
                );
                Self::notify_change(&change_ref);
                toast_ref.add_toast(adw::Toast::new("Subscription deleted"));
            });
        }
        suffix_box.append(&del_btn);

        row.add_suffix(&suffix_box);
        row
    }

    fn show_add_dialog(
        parent: &gtk::Button,
        db: &Rc<Database>,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        monthly_label: &gtk::Label,
        yearly_label: &gtk::Label,
        base_currency: &str,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        let dialog = adw::Dialog::builder()
            .title("Add Subscription")
            .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);

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

        // Name
        let name_row = adw::EntryRow::builder()
            .title("Name")
            .build();
        form1.add(&name_row);

        // 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 cat_model = gtk::StringList::new(&[]);
        let cat_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(),
                };
                cat_model.append(&entry);
                cat_ids.borrow_mut().push(cat.id);
            }
        }
        let cat_row = adw::ComboRow::builder()
            .title("Category")
            .model(&cat_model)
            .build();
        cat_row.set_factory(Some(&Self::make_category_factory()));
        cat_row.set_list_factory(Some(&Self::make_category_factory()));
        form1.add(&cat_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(3u32) // Monthly default
            .build();
        form1.add(&freq_row);

        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(&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_cur = 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_cur)) {
            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
        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);

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

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

        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 name_ref = name_row.clone();
            let amount_ref = amount_row.clone();
            let ids_ref = cat_ids.clone();
            let cat_ref = cat_row.clone();
            let toast_ref = toast_overlay.clone();
            next_btn.connect_clicked(move |_| {
                if name_ref.text().trim().is_empty() {
                    toast_ref.add_toast(adw::Toast::new("Please enter a name"));
                    return;
                }
                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;
                }
                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));

        {
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.to_string();
            let toast_ref = toast_overlay.clone();
            let cat_ids = cat_ids.clone();
            let currency_codes = currency_codes.clone();
            let change_ref = on_change.clone();
            save_btn.connect_clicked(move |_| {
                let name = name_row.text().to_string();
                if name.trim().is_empty() {
                    toast_ref.add_toast(adw::Toast::new("Please enter a name"));
                    return;
                }

                let amount_text = amount_row.text().to_string();
                let amount: f64 = match amount_text.parse() {
                    Ok(v) if v > 0.0 => v,
                    _ => {
                        toast_ref.add_toast(adw::Toast::new("Please enter a valid amount"));
                        return;
                    }
                };

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

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

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

                let start_str = start_date_label.text().to_string();
                let start_date = NaiveDate::parse_from_str(&start_str, "%Y-%m-%d")
                    .unwrap_or_else(|_| chrono::Local::now().date_naive());

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

                let new_sub = NewSubscription {
                    name: name.trim().to_string(),
                    amount,
                    currency,
                    frequency,
                    category_id,
                    start_date,
                    note,
                    url,
                    recurring_id: None,
                };

                let sub_cat_id = db_ref.find_subscriptions_category_id()
                    .ok().flatten()
                    .unwrap_or(category_id);

                match db_ref.insert_linked_subscription_and_recurring(&new_sub, sub_cat_id) {
                    Ok(_) => {
                        dialog_ref.close();
                        Self::load_subscriptions(
                            &db_ref, &active_ref, &paused_ref,
                            &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                            &change_ref,
                        );
                        Self::notify_change(&change_ref);
                        toast_ref.add_toast(adw::Toast::new("Subscription added"));
                    }
                    Err(e) => {
                        toast_ref.add_toast(adw::Toast::new(&format!("Error: {}", e)));
                    }
                }
            });
        }

        dialog.present(Some(parent));
    }

    fn show_edit_dialog(
        parent: &gtk::Button,
        db: &Rc<Database>,
        sub: &Subscription,
        active_group: &adw::PreferencesGroup,
        paused_group: &adw::PreferencesGroup,
        monthly_label: &gtk::Label,
        yearly_label: &gtk::Label,
        base_currency: &str,
        toast_overlay: &adw::ToastOverlay,
        on_change: &ChangeCb,
    ) {
        let dialog = adw::Dialog::builder()
            .title("Edit Subscription")
            .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();

        // Name
        let name_row = adw::EntryRow::builder()
            .title("Name")
            .text(&sub.name)
            .build();
        form.add(&name_row);

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

        // Currency
        let mut currencies = ExchangeRateService::supported_currencies();
        let base_cur = db.get_setting("base_currency")
            .ok().flatten()
            .unwrap_or_else(|| "USD".to_string());
        if let Some(pos) = currencies.iter().position(|(c, _)| c.eq_ignore_ascii_case(&base_cur)) {
            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 selected_currency = currency_codes.iter().position(|c| c == &sub.currency).unwrap_or(0) as u32;
        let currency_row = adw::ComboRow::builder()
            .title("Currency")
            .model(&currency_model)
            .selected(selected_currency)
            .build();
        form.add(&currency_row);

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

        // Category
        let cat_model = gtk::StringList::new(&[]);
        let cat_ids: Rc<RefCell<Vec<i64>>> = Rc::new(RefCell::new(Vec::new()));
        let mut selected_cat = 0u32;
        if let Ok(cats) = db.list_subscription_categories() {
            for (i, cat) in cats.iter().enumerate() {
                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(),
                };
                cat_model.append(&entry);
                cat_ids.borrow_mut().push(cat.id);
                if cat.id == sub.category_id {
                    selected_cat = i as u32;
                }
            }
        }
        let cat_row = adw::ComboRow::builder()
            .title("Category")
            .model(&cat_model)
            .selected(selected_cat)
            .build();
        cat_row.set_factory(Some(&Self::make_category_factory()));
        cat_row.set_list_factory(Some(&Self::make_category_factory()));
        form.add(&cat_row);

        // Start date
        let start_str = sub.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);

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

        // URL (optional)
        let url_row = adw::EntryRow::builder()
            .title("URL (optional)")
            .text(sub.url.as_deref().unwrap_or(""))
            .build();
        form.add(&url_row);

        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);

        content.append(&form);
        content.append(&save_btn);

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

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

        {
            let sub_id = sub.id;
            let sub_active = sub.active;
            let sub_recurring_id = sub.recurring_id;
            let db_ref = db.clone();
            let dialog_ref = dialog.clone();
            let active_ref = active_group.clone();
            let paused_ref = paused_group.clone();
            let monthly_ref = monthly_label.clone();
            let yearly_ref = yearly_label.clone();
            let base_ref = base_currency.to_string();
            let toast_ref = toast_overlay.clone();
            let cat_ids = cat_ids.clone();
            let currency_codes = currency_codes.clone();
            let change_ref = on_change.clone();
            save_btn.connect_clicked(move |_| {
                let name = name_row.text().to_string();
                if name.trim().is_empty() {
                    toast_ref.add_toast(adw::Toast::new("Please enter a name"));
                    return;
                }

                let amount_text = amount_row.text().to_string();
                let amount: f64 = match amount_text.parse() {
                    Ok(v) if v > 0.0 => v,
                    _ => {
                        toast_ref.add_toast(adw::Toast::new("Please enter a valid amount"));
                        return;
                    }
                };

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

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

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

                let start_str = start_date_label.text().to_string();
                let start_date = NaiveDate::parse_from_str(&start_str, "%Y-%m-%d")
                    .unwrap_or_else(|_| chrono::Local::now().date_naive());

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

                let updated = Subscription {
                    id: sub_id,
                    name: name.trim().to_string(),
                    amount,
                    currency: currency.clone(),
                    frequency,
                    category_id,
                    start_date,
                    next_due: start_date,
                    active: sub_active,
                    note: note.clone(),
                    url,
                    recurring_id: sub_recurring_id,
                };

                match db_ref.update_subscription(&updated) {
                    Ok(_) => {
                        if let Some(rec_id) = sub_recurring_id {
                            if let Ok(mut rec) = db_ref.get_recurring(rec_id) {
                                rec.amount = amount;
                                rec.frequency = frequency;
                                rec.currency = currency;
                                rec.note = note;
                                let _ = db_ref.update_recurring(&rec);
                            }
                        }
                        dialog_ref.close();
                        Self::load_subscriptions(
                            &db_ref, &active_ref, &paused_ref,
                            &monthly_ref, &yearly_ref, &base_ref, &toast_ref,
                            &change_ref,
                        );
                        Self::notify_change(&change_ref);
                        toast_ref.add_toast(adw::Toast::new("Subscription updated"));
                    }
                    Err(e) => {
                        toast_ref.add_toast(adw::Toast::new(&format!("Error: {}", e)));
                    }
                }
            });
        }

        dialog.present(Some(parent));
    }

    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();
        }
    }
}
