use adw::prelude::*;
use gtk::gio;
use std::cell::Cell;
use std::rc::Rc;

use crate::core::database::Database;
use crate::core::updater;
use crate::i18n::{i18n, i18n_f};
use super::widgets;

/// Show a dialog to update all AppImages that have updates available.
pub fn show_batch_update_dialog(parent: &impl IsA<gtk::Widget>, db: &Rc<Database>) {
    let records = db.get_all_appimages().unwrap_or_default();
    let updatable: Vec<_> = records
        .into_iter()
        .filter(|r| {
            if let (Some(ref latest), Some(ref current)) = (&r.latest_version, &r.app_version) {
                r.update_url.is_some() && updater::version_is_newer(latest, current)
            } else {
                false
            }
        })
        .collect();

    if updatable.is_empty() {
        let dialog = adw::AlertDialog::builder()
            .heading(&i18n("No Updates"))
            .body(&i18n("All AppImages are up to date."))
            .build();
        dialog.add_response("ok", &i18n("OK"));
        dialog.present(Some(parent));
        return;
    }

    let dialog = adw::Dialog::builder()
        .title(&i18n("Update All"))
        .content_width(500)
        .content_height(450)
        .build();

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

    let content = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .spacing(12)
        .margin_start(18)
        .margin_end(18)
        .margin_top(12)
        .margin_bottom(12)
        .build();

    let status_label = gtk::Label::builder()
        .label(&i18n_f(
            "{count} updates available",
            &[("{count}", &updatable.len().to_string())],
        ))
        .xalign(0.0)
        .build();
    status_label.add_css_class("title-4");
    content.append(&status_label);

    let overall_progress = gtk::ProgressBar::builder()
        .show_text(true)
        .text(&i18n("Ready"))
        .build();
    overall_progress.update_property(&[gtk::accessible::Property::Label("Overall update progress")]);
    content.append(&overall_progress);

    // List of apps to update
    let list_box = gtk::ListBox::new();
    list_box.add_css_class("boxed-list");
    list_box.set_selection_mode(gtk::SelectionMode::None);
    list_box.update_property(&[
        gtk::accessible::Property::Label(&i18n("Apps to update")),
    ]);

    let mut row_data: Vec<(i64, String, String, String, adw::ActionRow, gtk::Label)> = Vec::new();

    for record in &updatable {
        let name = record.app_name.as_deref().unwrap_or(&record.filename);
        let current_ver = record.app_version.as_deref().unwrap_or("?");
        let latest_ver = record.latest_version.as_deref().unwrap_or("?");

        let row = adw::ActionRow::builder()
            .title(name)
            .subtitle(&format!("{} -> {}", current_ver, latest_ver))
            .build();

        let status_badge = gtk::Label::builder()
            .label(&i18n("Pending"))
            .valign(gtk::Align::Center)
            .accessible_role(gtk::AccessibleRole::Status)
            .build();
        status_badge.add_css_class("dim-label");
        row.add_suffix(&status_badge);

        list_box.append(&row);
        row_data.push((
            record.id,
            record.path.clone(),
            record.update_url.clone().unwrap_or_default(),
            record.latest_version.clone().unwrap_or_default(),
            row,
            status_badge,
        ));
    }

    let scrolled = gtk::ScrolledWindow::builder()
        .child(&list_box)
        .vexpand(true)
        .min_content_height(200)
        .build();
    content.append(&scrolled);

    // Buttons
    let button_box = gtk::Box::builder()
        .orientation(gtk::Orientation::Horizontal)
        .spacing(12)
        .halign(gtk::Align::End)
        .margin_top(6)
        .build();

    let cancel_btn = gtk::Button::builder()
        .label(&i18n("Cancel"))
        .build();

    let update_btn = gtk::Button::builder()
        .label(&i18n("Update All"))
        .build();
    update_btn.add_css_class("suggested-action");

    button_box.append(&cancel_btn);
    button_box.append(&update_btn);
    content.append(&button_box);

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

    let dialog_weak = dialog.downgrade();
    cancel_btn.connect_clicked(move |_| {
        if let Some(d) = dialog_weak.upgrade() {
            d.close();
        }
    });

    let cancelled = Rc::new(Cell::new(false));
    let cancelled_close = cancelled.clone();
    // Mark cancelled when dialog is closed during update
    dialog.connect_closed(move |_| {
        cancelled_close.set(true);
    });

    let dialog_update_weak = dialog.downgrade();
    update_btn.connect_clicked(move |btn| {
        btn.set_sensitive(false);

        let total = row_data.len();
        let progress_bar = overall_progress.clone();
        let status = status_label.clone();
        let cancelled = cancelled.clone();
        let row_data_clone: Vec<(i64, String, String, String, adw::ActionRow, gtk::Label)> =
            row_data.iter().map(|(id, path, url, ver, row, badge)| {
                (*id, path.clone(), url.clone(), ver.clone(), row.clone(), badge.clone())
            }).collect();
        let dialog_weak = dialog_update_weak.clone();

        glib::spawn_future_local(async move {
            let mut completed = 0usize;
            let mut succeeded = 0usize;
            let mut failed = 0usize;

            for (record_id, path, url, new_version, _row, badge) in &row_data_clone {
                if cancelled.get() {
                    break;
                }

                badge.set_label(&i18n("Updating..."));
                badge.remove_css_class("dim-label");
                badge.add_css_class("accent");

                let path = path.clone();
                let url = url.clone();
                let version = new_version.clone();
                let record_id = *record_id;

                let result = gio::spawn_blocking(move || {
                    let p = std::path::Path::new(&path);
                    let update_result = updater::perform_update(
                        p,
                        Some(&url),
                        true, // keep old for rollback
                        None,
                    );

                    // On success, record rollback path and clear update flag
                    if let Ok(ref applied) = update_result {
                        let bg_db = Database::open().expect("DB open failed");
                        if let Some(ref old_path) = applied.old_path_backup {
                            bg_db.set_previous_version(
                                record_id,
                                Some(&old_path.to_string_lossy()),
                            ).ok();
                        }
                        let actual_ver = applied.new_version.as_deref()
                            .unwrap_or(&version);
                        bg_db.record_update(
                            record_id,
                            None,
                            Some(actual_ver),
                            Some("batch-update"),
                            None,
                            true,
                        ).ok();
                        bg_db.clear_update_available(record_id).ok();
                    }

                    update_result
                })
                .await;

                completed += 1;
                let fraction = completed as f64 / total as f64;
                progress_bar.set_fraction(fraction);
                progress_bar.set_text(Some(&format!("{}/{}", completed, total)));
                status.set_label(&i18n_f(
                    "Updating {current} of {total}...",
                    &[
                        ("{current}", &completed.to_string()),
                        ("{total}", &total.to_string()),
                    ],
                ));

                match result {
                    Ok(Ok(_)) => {
                        badge.set_label(&i18n("Done"));
                        badge.remove_css_class("accent");
                        badge.add_css_class("success");
                        succeeded += 1;
                    }
                    _ => {
                        badge.set_label(&i18n("Failed"));
                        badge.remove_css_class("accent");
                        badge.add_css_class("error");
                        failed += 1;
                    }
                }
            }

            // Final status
            let summary = if failed == 0 {
                i18n_f(
                    "All {count} updates applied successfully",
                    &[("{count}", &succeeded.to_string())],
                )
            } else {
                i18n_f(
                    "{ok} succeeded, {fail} failed",
                    &[
                        ("{ok}", &succeeded.to_string()),
                        ("{fail}", &failed.to_string()),
                    ],
                )
            };
            status.set_label(&summary);
            progress_bar.set_text(Some(&i18n("Complete")));
            progress_bar.set_fraction(1.0);
            widgets::announce(progress_bar.upcast_ref::<gtk::Widget>(), &summary);

            // Change cancel to Close
            if let Some(d) = dialog_weak.upgrade() {
                // The user can now close via the header bar
                let _ = d;
            }
        });
    });

    dialog.present(Some(parent));
}
