use adw::prelude::*;
use chrono::{Datelike, Local};
use outlay_core::db::Database;
use outlay_core::exchange::ExchangeRateService;
use std::rc::Rc;

pub struct CreditCardsView {
    pub container: gtk::Box,
}

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

        // Summary card
        let summary_group = adw::PreferencesGroup::builder()
            .title("SUMMARY")
            .build();

        let total_balance_row = adw::ActionRow::builder()
            .title("Total Balance")
            .build();
        let balance_label = gtk::Label::new(Some("0.00"));
        balance_label.add_css_class("amount-display");
        total_balance_row.add_suffix(&balance_label);

        let total_limit_row = adw::ActionRow::builder()
            .title("Total Credit Limit")
            .build();
        let limit_label = gtk::Label::new(Some("0.00"));
        limit_label.add_css_class("dim-label");
        total_limit_row.add_suffix(&limit_label);

        let utilization_row = adw::ActionRow::builder()
            .title("Overall Utilization")
            .build();
        let util_bar = gtk::LevelBar::new();
        util_bar.set_min_value(0.0);
        util_bar.set_max_value(1.0);
        util_bar.set_hexpand(true);
        util_bar.set_valign(gtk::Align::Center);
        let util_label = gtk::Label::new(Some("0%"));
        util_label.add_css_class("caption");
        util_label.set_margin_start(8);
        let util_box = gtk::Box::new(gtk::Orientation::Horizontal, 4);
        util_box.append(&util_bar);
        util_box.append(&util_label);
        utilization_row.add_suffix(&util_box);

        summary_group.add(&total_balance_row);
        summary_group.add(&total_limit_row);
        summary_group.add(&utilization_row);

        // Cards list
        let cards_group = adw::PreferencesGroup::builder()
            .title("CARDS")
            .build();

        // Populate
        Self::populate_cards(
            &db, &cards_group, &toast_overlay,
            &balance_label, &limit_label, &util_bar, &util_label,
        );

        // Add card button
        let add_btn = gtk::Button::with_label("Add Card");
        add_btn.add_css_class("pill");
        add_btn.set_halign(gtk::Align::Center);
        add_btn.set_margin_top(8);
        {
            let db_ref = db.clone();
            let group_ref = cards_group.clone();
            let toast_ref = toast_overlay.clone();
            let bl = balance_label.clone();
            let ll = limit_label.clone();
            let ub = util_bar.clone();
            let ul = util_label.clone();
            add_btn.connect_clicked(move |btn| {
                Self::show_card_dialog(
                    btn, &db_ref, None, &group_ref, &toast_ref,
                    &bl, &ll, &ub, &ul,
                );
            });
        }

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

        CreditCardsView { container }
    }

    fn populate_cards(
        db: &Rc<Database>,
        group: &adw::PreferencesGroup,
        toast: &adw::ToastOverlay,
        balance_label: &gtk::Label,
        limit_label: &gtk::Label,
        util_bar: &gtk::LevelBar,
        util_label: &gtk::Label,
    ) {
        // Remove existing rows
        while let Some(child) = group.first_child() {
            if let Some(inner) = child.first_child() {
                if let Some(listbox) = inner.downcast_ref::<gtk::ListBox>() {
                    while let Some(row) = listbox.row_at_index(0) {
                        listbox.remove(&row);
                    }
                }
            }
            break;
        }

        let cards = db.list_credit_cards().unwrap_or_default();
        let today = Local::now().date_naive();

        let mut total_balance = 0.0_f64;
        let mut total_limit = 0.0_f64;

        for card in &cards {
            total_balance += card.current_balance;
            if let Some(lim) = card.credit_limit {
                total_limit += lim;
            }

            // Utilization for this card
            let card_util = if let Some(lim) = card.credit_limit {
                if lim > 0.0 { card.current_balance / lim } else { 0.0 }
            } else {
                0.0
            };

            // Days until due
            let due_day = card.due_day as u32;
            let current_day = today.day();
            let days_until_due = if due_day > current_day {
                due_day - current_day
            } else if due_day == current_day {
                0
            } else {
                // Next month
                let days_in_month = {
                    let (y, m) = if today.month() == 12 { (today.year() + 1, 1) } else { (today.year(), today.month() + 1) };
                    chrono::NaiveDate::from_ymd_opt(y, m, 1).unwrap().pred_opt().unwrap().day()
                };
                days_in_month - current_day + due_day
            };

            let subtitle = format!(
                "{:.2} {} - Due in {} day{}",
                card.current_balance,
                card.currency,
                days_until_due,
                if days_until_due == 1 { "" } else { "s" },
            );

            let expander = adw::ExpanderRow::builder()
                .title(&card.name)
                .subtitle(&subtitle)
                .build();

            // Utilization bar in suffix
            let mini_bar = gtk::LevelBar::new();
            mini_bar.set_min_value(0.0);
            mini_bar.set_max_value(1.0);
            mini_bar.set_value(card_util.min(1.0));
            mini_bar.set_size_request(60, -1);
            mini_bar.set_valign(gtk::Align::Center);
            expander.add_suffix(&mini_bar);

            // Expanded content
            let close_row = adw::ActionRow::builder()
                .title("Statement Close Day")
                .subtitle(&format!("Day {} of each month", card.statement_close_day))
                .build();
            expander.add_row(&close_row);

            let due_row = adw::ActionRow::builder()
                .title("Payment Due Day")
                .subtitle(&format!("Day {} of each month", card.due_day))
                .build();
            expander.add_row(&due_row);

            if let Some(lim) = card.credit_limit {
                let limit_row = adw::ActionRow::builder()
                    .title("Credit Limit")
                    .subtitle(&format!("{:.2} {}", lim, card.currency))
                    .build();
                expander.add_row(&limit_row);
            }

            let min_pmt = card.min_payment_pct * card.current_balance / 100.0;
            let min_row = adw::ActionRow::builder()
                .title("Minimum Payment")
                .subtitle(&format!("{:.2} {} ({:.1}%)", min_pmt, card.currency, card.min_payment_pct))
                .build();
            expander.add_row(&min_row);

            // Action buttons
            let btn_box = gtk::Box::new(gtk::Orientation::Horizontal, 8);
            btn_box.set_halign(gtk::Align::Center);
            btn_box.set_margin_top(8);
            btn_box.set_margin_bottom(8);

            let pay_btn = gtk::Button::with_label("Record Payment");
            pay_btn.add_css_class("suggested-action");
            pay_btn.add_css_class("pill");

            let edit_btn = gtk::Button::with_label("Edit");
            edit_btn.add_css_class("pill");

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

            btn_box.append(&pay_btn);
            btn_box.append(&edit_btn);
            btn_box.append(&del_btn);

            let btn_row = adw::ActionRow::new();
            btn_row.set_child(Some(&btn_box));
            expander.add_row(&btn_row);

            // Wire payment
            {
                let card_id = card.id;
                let card_name = card.name.clone();
                let db_ref = db.clone();
                let group_ref = group.clone();
                let toast_ref = toast.clone();
                let bl = balance_label.clone();
                let ll = limit_label.clone();
                let ub = util_bar.clone();
                let ul = util_label.clone();
                pay_btn.connect_clicked(move |btn| {
                    Self::show_payment_dialog(
                        btn, &db_ref, card_id, &card_name,
                        &group_ref, &toast_ref, &bl, &ll, &ub, &ul,
                    );
                });
            }

            // Wire edit
            {
                let card_id = card.id;
                let db_ref = db.clone();
                let group_ref = group.clone();
                let toast_ref = toast.clone();
                let bl = balance_label.clone();
                let ll = limit_label.clone();
                let ub = util_bar.clone();
                let ul = util_label.clone();
                edit_btn.connect_clicked(move |btn| {
                    Self::show_card_dialog(
                        btn, &db_ref, Some(card_id), &group_ref, &toast_ref,
                        &bl, &ll, &ub, &ul,
                    );
                });
            }

            // Wire delete
            {
                let card_id = card.id;
                let db_ref = db.clone();
                let group_ref = group.clone();
                let toast_ref = toast.clone();
                let bl = balance_label.clone();
                let ll = limit_label.clone();
                let ub = util_bar.clone();
                let ul = util_label.clone();
                del_btn.connect_clicked(move |btn| {
                    let alert = adw::AlertDialog::new(
                        Some("Delete this card?"),
                        Some("This cannot be undone."),
                    );
                    alert.add_response("cancel", "Cancel");
                    alert.add_response("delete", "Delete");
                    alert.set_response_appearance("delete", adw::ResponseAppearance::Destructive);
                    alert.set_default_response(Some("cancel"));

                    let db_c = db_ref.clone();
                    let g = group_ref.clone();
                    let t = toast_ref.clone();
                    let bl = bl.clone();
                    let ll = ll.clone();
                    let ub = ub.clone();
                    let ul = ul.clone();
                    alert.connect_response(None, move |_, resp| {
                        if resp == "delete" {
                            if db_c.delete_credit_card(card_id).is_ok() {
                                Self::populate_cards(&db_c, &g, &t, &bl, &ll, &ub, &ul);
                                t.add_toast(adw::Toast::new("Card deleted"));
                            }
                        }
                    });
                    alert.present(Some(btn));
                });
            }

            group.add(&expander);
        }

        // Update summary
        balance_label.set_label(&format!("{:.2}", total_balance));
        limit_label.set_label(&format!("{:.2}", total_limit));
        let util = if total_limit > 0.0 { total_balance / total_limit } else { 0.0 };
        util_bar.set_value(util.min(1.0));
        util_label.set_label(&format!("{:.0}%", util * 100.0));

        if cards.is_empty() {
            let empty = adw::ActionRow::builder()
                .title("No credit cards")
                .subtitle("Add a card to track billing cycles and payments")
                .build();
            group.add(&empty);
        }
    }

    fn show_payment_dialog(
        parent: &impl IsA<gtk::Widget>,
        db: &Rc<Database>,
        card_id: i64,
        card_name: &str,
        group: &adw::PreferencesGroup,
        toast: &adw::ToastOverlay,
        bl: &gtk::Label,
        ll: &gtk::Label,
        ub: &gtk::LevelBar,
        ul: &gtk::Label,
    ) {
        let alert = adw::AlertDialog::new(
            Some("Record Payment"),
            Some(&format!("Enter payment amount for {}", card_name)),
        );
        alert.add_response("cancel", "Cancel");
        alert.add_response("pay", "Record");
        alert.set_response_appearance("pay", adw::ResponseAppearance::Suggested);
        alert.set_default_response(Some("pay"));

        let entry = adw::EntryRow::builder()
            .title("Amount")
            .build();
        entry.set_input_purpose(gtk::InputPurpose::Number);
        alert.set_extra_child(Some(&entry));

        let db_ref = db.clone();
        let group_ref = group.clone();
        let toast_ref = toast.clone();
        let card_name = card_name.to_string();
        let bl = bl.clone();
        let ll = ll.clone();
        let ub = ub.clone();
        let ul = ul.clone();
        alert.connect_response(None, move |_, resp| {
            if resp == "pay" {
                let text = entry.text();
                if let Some(amount) = outlay_core::expr::eval_expr(&text) {
                    if amount > 0.0 {
                        if db_ref.record_card_payment(card_id, amount).is_ok() {
                            // Create expense transaction for the payment
                            let today = Local::now().date_naive();
                            let base_currency = db_ref.get_setting("base_currency")
                                .ok().flatten()
                                .unwrap_or_else(|| "USD".to_string());
                            // Use first expense category as fallback
                            let cat_id = db_ref.list_categories(Some(outlay_core::models::TransactionType::Expense))
                                .unwrap_or_default()
                                .first()
                                .map(|c| c.id)
                                .unwrap_or(1);
                            let txn = outlay_core::models::NewTransaction {
                                amount,
                                transaction_type: outlay_core::models::TransactionType::Expense,
                                category_id: cat_id,
                                currency: base_currency,
                                exchange_rate: 1.0,
                                note: Some(format!("Credit card payment - {}", card_name)),
                                date: today,
                                recurring_id: None,
                                payee: None,
                            };
                            let _ = db_ref.insert_transaction(&txn);
                            Self::populate_cards(&db_ref, &group_ref, &toast_ref, &bl, &ll, &ub, &ul);
                            toast_ref.add_toast(adw::Toast::new(&format!("Payment of {:.2} recorded", amount)));
                        }
                    }
                }
            }
        });
        alert.present(Some(parent));
    }

    fn show_card_dialog(
        parent: &impl IsA<gtk::Widget>,
        db: &Rc<Database>,
        card_id: Option<i64>,
        group: &adw::PreferencesGroup,
        toast: &adw::ToastOverlay,
        bl: &gtk::Label,
        ll: &gtk::Label,
        ub: &gtk::LevelBar,
        ul: &gtk::Label,
    ) {
        let existing = card_id.and_then(|id| db.get_credit_card(id).ok());
        let is_edit = existing.is_some();

        let dialog = adw::AlertDialog::new(
            Some(if is_edit { "Edit Card" } else { "Add Card" }),
            None,
        );
        dialog.add_response("cancel", "Cancel");
        dialog.add_response("save", if is_edit { "Save" } else { "Add" });
        dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested);
        dialog.set_default_response(Some("save"));

        let form = gtk::Box::new(gtk::Orientation::Vertical, 8);

        let name_entry = adw::EntryRow::builder()
            .title("Card Name")
            .text(existing.as_ref().map(|c| c.name.as_str()).unwrap_or(""))
            .build();

        let existing_currency = existing.as_ref().map(|c| c.currency.as_str()).unwrap_or("USD");
        let limit_entry = adw::EntryRow::builder()
            .title(&format!("Credit Limit ({})", existing_currency))
            .text(&existing.as_ref().and_then(|c| c.credit_limit).map(|l| format!("{:.2}", l)).unwrap_or_default())
            .build();
        limit_entry.set_input_purpose(gtk::InputPurpose::Number);
        crate::numpad::attach_numpad(&limit_entry);

        let close_spin = adw::SpinRow::with_range(1.0, 31.0, 1.0);
        close_spin.set_title("Statement Close Day");
        close_spin.set_value(existing.as_ref().map(|c| c.statement_close_day as f64).unwrap_or(25.0));

        let due_spin = adw::SpinRow::with_range(1.0, 31.0, 1.0);
        due_spin.set_title("Payment Due Day");
        due_spin.set_value(existing.as_ref().map(|c| c.due_day as f64).unwrap_or(15.0));

        let min_spin = adw::SpinRow::with_range(0.0, 100.0, 0.5);
        min_spin.set_title("Minimum Payment %");
        min_spin.set_value(existing.as_ref().map(|c| c.min_payment_pct).unwrap_or(2.0));

        let currencies = ExchangeRateService::supported_currencies();
        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_idx = currency_codes
            .iter()
            .position(|c| c.eq_ignore_ascii_case(existing_currency))
            .unwrap_or(0);

        let currency_combo = adw::ComboRow::builder()
            .title("Currency")
            .model(&currency_model)
            .selected(currency_idx as u32)
            .build();

        {
            let limit_ref = limit_entry.clone();
            let codes = currency_codes.clone();
            currency_combo.connect_selected_notify(move |combo| {
                if let Some(code) = codes.get(combo.selected() as usize) {
                    limit_ref.set_title(&format!("Credit Limit ({})", code));
                }
            });
        }

        let list = gtk::ListBox::new();
        list.add_css_class("boxed-list");
        list.set_selection_mode(gtk::SelectionMode::None);
        list.append(&name_entry);
        list.append(&limit_entry);
        list.append(&close_spin);
        list.append(&due_spin);
        list.append(&min_spin);
        list.append(&currency_combo);

        form.append(&list);
        dialog.set_extra_child(Some(&form));

        let db_ref = db.clone();
        let group_ref = group.clone();
        let toast_ref = toast.clone();
        let bl = bl.clone();
        let ll = ll.clone();
        let ub = ub.clone();
        let ul = ul.clone();
        dialog.connect_response(None, move |_, resp| {
            if resp == "save" {
                let name = name_entry.text().to_string();
                if name.trim().is_empty() {
                    toast_ref.add_toast(adw::Toast::new("Card name is required"));
                    return;
                }
                let limit_text = limit_entry.text().to_string();
                let credit_limit = if limit_text.is_empty() {
                    None
                } else {
                    limit_text.parse::<f64>().ok()
                };
                let close_day = close_spin.value() as i32;
                let due_day = due_spin.value() as i32;
                let min_pct = min_spin.value();
                let currency = currency_codes
                    .get(currency_combo.selected() as usize)
                    .cloned()
                    .unwrap_or_else(|| "USD".to_string());

                if let Some(id) = card_id {
                    let card = outlay_core::models::CreditCard {
                        id,
                        name: name.trim().to_string(),
                        credit_limit,
                        statement_close_day: close_day,
                        due_day,
                        min_payment_pct: min_pct,
                        current_balance: existing.as_ref().map(|c| c.current_balance).unwrap_or(0.0),
                        currency,
                        color: None,
                        active: true,
                    };
                    let _ = db_ref.update_credit_card(&card);
                } else {
                    let card = outlay_core::models::NewCreditCard {
                        name: name.trim().to_string(),
                        credit_limit,
                        statement_close_day: close_day,
                        due_day,
                        min_payment_pct: min_pct,
                        currency,
                        color: None,
                    };
                    let _ = db_ref.insert_credit_card(&card);
                }
                Self::populate_cards(&db_ref, &group_ref, &toast_ref, &bl, &ll, &ub, &ul);
                toast_ref.add_toast(adw::Toast::new(if is_edit { "Card updated" } else { "Card added" }));
            }
        });
        dialog.present(Some(parent));
    }
}
