use adw::prelude::*;
use gtk::gio;

use crate::config::APP_ID;
use crate::core::appstream;
use crate::core::database::Database;
use crate::i18n::i18n;

pub fn show_preferences_dialog(parent: &impl IsA<gtk::Widget>) {
    let dialog = adw::PreferencesDialog::new();
    dialog.set_title(&i18n("Preferences"));

    let settings = gio::Settings::new(APP_ID);

    dialog.add(&build_general_page(&settings, &dialog));
    dialog.add(&build_updates_page(&settings));
    super::widgets::apply_pointer_cursors(&dialog);

    dialog.present(Some(parent));
}

// --- General page ---

fn build_general_page(settings: &gio::Settings, dialog: &adw::PreferencesDialog) -> adw::PreferencesPage {
    let page = adw::PreferencesPage::builder()
        .title(&i18n("General"))
        .icon_name("emblem-system-symbolic")
        .build();

    // Appearance group
    let appearance_group = adw::PreferencesGroup::builder()
        .title(&i18n("Appearance"))
        .build();

    let theme_row = adw::ComboRow::builder()
        .title(&i18n("Color Scheme"))
        .subtitle(&i18n("Choose light, dark, or follow system preference"))
        .build();

    let model = gtk::StringList::new(&[&i18n("Follow System"), &i18n("Light"), &i18n("Dark")]);
    theme_row.set_model(Some(&model));

    let current = settings.string("color-scheme");
    theme_row.set_selected(match current.as_str() {
        "force-light" => 1,
        "force-dark" => 2,
        _ => 0,
    });

    let settings_clone = settings.clone();
    theme_row.connect_selected_notify(move |row| {
        let value = match row.selected() {
            1 => "force-light",
            2 => "force-dark",
            _ => "default",
        };
        settings_clone.set_string("color-scheme", value).ok();
    });

    appearance_group.add(&theme_row);

    let view_row = adw::ComboRow::builder()
        .title(&i18n("Default View"))
        .subtitle(&i18n("Library display mode"))
        .build();
    let view_model = gtk::StringList::new(&[&i18n("Grid"), &i18n("List")]);
    view_row.set_model(Some(&view_model));
    let current_view = settings.string("view-mode");
    view_row.set_selected(if current_view.as_str() == "list" { 1 } else { 0 });

    let settings_view = settings.clone();
    view_row.connect_selected_notify(move |row| {
        let value = if row.selected() == 1 { "list" } else { "grid" };
        settings_view.set_string("view-mode", value).ok();
    });

    appearance_group.add(&view_row);

    page.add(&appearance_group);

    // Scan Locations group
    let scan_group = adw::PreferencesGroup::builder()
        .title(&i18n("Scan Locations"))
        .description(&i18n("Folders where Driftwood looks for AppImage files. Add any folder where you save downloaded apps."))
        .build();

    let dirs = settings.strv("scan-directories");
    let dir_list_box = gtk::ListBox::new();
    dir_list_box.add_css_class("boxed-list");
    dir_list_box.set_selection_mode(gtk::SelectionMode::None);
    dir_list_box.update_property(&[
        gtk::accessible::Property::Label(&i18n("Scan directories")),
    ]);

    for dir in &dirs {
        add_directory_row(&dir_list_box, &dir, settings);
    }

    scan_group.add(&dir_list_box);

    // Add location button
    let add_button = gtk::Button::builder()
        .label(&i18n("Add Location"))
        .build();
    add_button.add_css_class("flat");
    add_button.update_property(&[
        gtk::accessible::Property::Description(&i18n("Add a new directory to scan for AppImages")),
    ]);

    let settings_add = settings.clone();
    let list_box_ref = dir_list_box.clone();
    let dialog_weak = dialog.downgrade();
    add_button.connect_clicked(move |_| {
        let file_dialog = gtk::FileDialog::builder()
            .title(i18n("Choose a directory"))
            .modal(true)
            .build();

        let settings_ref = settings_add.clone();
        let list_ref = list_box_ref.clone();
        let dlg = dialog_weak.upgrade();
        let parent_window: Option<gtk::Window> = dlg
            .as_ref()
            .and_then(|d| d.root())
            .and_then(|r| r.downcast::<gtk::Window>().ok());
        file_dialog.select_folder(
            parent_window.as_ref(),
            None::<&gio::Cancellable>,
            move |result| {
                if let Ok(file) = result {
                    if let Some(path) = file.path() {
                        let path_str = path.to_string_lossy().to_string();

                        let mut current_dirs: Vec<String> = settings_ref
                            .strv("scan-directories")
                            .iter()
                            .map(|s| s.to_string())
                            .collect();

                        if !current_dirs.contains(&path_str) {
                            current_dirs.push(path_str.clone());
                            let refs: Vec<&str> =
                                current_dirs.iter().map(|s| s.as_str()).collect();
                            settings_ref.set_strv("scan-directories", refs).ok();

                            add_directory_row(&list_ref, &path_str, &settings_ref);
                        }
                    }
                }
            },
        );
    });

    scan_group.add(&add_button);
    page.add(&scan_group);

    // Automation group
    let automation_group = adw::PreferencesGroup::builder()
        .title(&i18n("Automation"))
        .description(&i18n("Control what happens automatically when Driftwood starts or finds new apps."))
        .build();

    let auto_scan_row = adw::SwitchRow::builder()
        .title(&i18n("Scan on startup"))
        .subtitle(&i18n("Automatically scan for new AppImages when the app starts"))
        .active(settings.boolean("auto-scan-on-startup"))
        .build();
    let settings_scan = settings.clone();
    auto_scan_row.connect_active_notify(move |row| {
        settings_scan.set_boolean("auto-scan-on-startup", row.is_active()).ok();
    });
    automation_group.add(&auto_scan_row);

    let auto_integrate_row = adw::SwitchRow::builder()
        .title(&i18n("Auto-integrate new AppImages"))
        .subtitle(&i18n("Automatically add newly discovered AppImages to the desktop menu"))
        .active(settings.boolean("auto-integrate"))
        .build();
    let settings_int = settings.clone();
    auto_integrate_row.connect_active_notify(move |row| {
        settings_int.set_boolean("auto-integrate", row.is_active()).ok();
    });
    automation_group.add(&auto_integrate_row);

    let removable_row = adw::SwitchRow::builder()
        .title(&i18n("Watch removable media"))
        .subtitle(&i18n("Scan USB drives and other removable media for AppImages"))
        .active(settings.boolean("watch-removable-media"))
        .build();
    let settings_rem = settings.clone();
    removable_row.connect_active_notify(move |row| {
        settings_rem.set_boolean("watch-removable-media", row.is_active()).ok();
    });
    automation_group.add(&removable_row);

    let confirm_row = adw::SwitchRow::builder()
        .title(&i18n("Confirm before delete"))
        .subtitle(&i18n("Show a confirmation dialog before deleting files or cleaning up"))
        .active(settings.boolean("confirm-before-delete"))
        .build();
    let settings_confirm = settings.clone();
    confirm_row.connect_active_notify(move |row| {
        settings_confirm.set_boolean("confirm-before-delete", row.is_active()).ok();
    });
    automation_group.add(&confirm_row);

    page.add(&automation_group);

    // Desktop Integration group
    let integration_group = adw::PreferencesGroup::builder()
        .title(&i18n("Desktop Integration"))
        .description(&i18n(
            "Make your AppImages visible in GNOME Software and KDE Discover",
        ))
        .build();

    let catalog_row = adw::SwitchRow::builder()
        .title(&i18n("AppStream catalog"))
        .subtitle(&i18n(
            "Generate a local catalog so software centers can list your AppImages",
        ))
        .active(appstream::is_catalog_installed())
        .build();

    catalog_row.connect_active_notify(|row| {
        let enable = row.is_active();
        glib::spawn_future_local(async move {
            let result = gio::spawn_blocking(move || {
                if enable {
                    let db = Database::open().expect("Failed to open database");
                    appstream::install_catalog(&db)
                        .map(|p| log::info!("AppStream catalog installed: {}", p.display()))
                        .map_err(|e| e.to_string())
                } else {
                    appstream::uninstall_catalog()
                        .map(|()| log::info!("AppStream catalog removed"))
                        .map_err(|e| e.to_string())
                }
            })
            .await;
            if let Ok(Err(e)) = result {
                log::warn!("AppStream catalog toggle failed: {}", e);
            }
        });
    });

    integration_group.add(&catalog_row);
    page.add(&integration_group);

    page
}

// --- Updates page ---

fn build_updates_page(settings: &gio::Settings) -> adw::PreferencesPage {
    let page = adw::PreferencesPage::builder()
        .title(&i18n("Updates"))
        .icon_name("software-update-available-symbolic")
        .build();

    // Update Checking group
    let checking_group = adw::PreferencesGroup::builder()
        .title(&i18n("Update Checking"))
        .description(&i18n("Let Driftwood periodically check if newer versions of your apps are available."))
        .build();

    let auto_update_row = adw::SwitchRow::builder()
        .title(&i18n("Check for updates"))
        .subtitle(&i18n("Periodically check if newer versions of your AppImages are available"))
        .active(settings.boolean("auto-check-updates"))
        .build();
    let settings_upd = settings.clone();
    auto_update_row.connect_active_notify(move |row| {
        settings_upd.set_boolean("auto-check-updates", row.is_active()).ok();
    });
    checking_group.add(&auto_update_row);

    let interval_row = adw::SpinRow::builder()
        .title(&i18n("Update check interval"))
        .subtitle(&i18n("Hours between automatic update checks"))
        .build();
    let interval_adj = gtk::Adjustment::new(
        settings.int("update-check-interval-hours") as f64,
        1.0,
        168.0,
        1.0,
        6.0,
        0.0,
    );
    interval_row.set_adjustment(Some(&interval_adj));
    let settings_interval = settings.clone();
    interval_row.connect_value_notify(move |row| {
        settings_interval.set_int("update-check-interval-hours", row.value() as i32).ok();
    });
    checking_group.add(&interval_row);

    page.add(&checking_group);

    // Update Behavior group
    let behavior_group = adw::PreferencesGroup::builder()
        .title(&i18n("Update Behavior"))
        .description(&i18n("Control what happens when an app is updated to a newer version."))
        .build();

    let cleanup_row = adw::ComboRow::builder()
        .title(&i18n("After updating an AppImage"))
        .subtitle(&i18n("What to do with the old version after a successful update"))
        .build();
    let cleanup_model = gtk::StringList::new(&[&i18n("Ask each time"), &i18n("Remove old version"), &i18n("Keep backup")]);
    cleanup_row.set_model(Some(&cleanup_model));

    let current_cleanup = settings.string("update-cleanup");
    cleanup_row.set_selected(match current_cleanup.as_str() {
        "always" => 1,
        "never" => 2,
        _ => 0,
    });

    let settings_cleanup = settings.clone();
    cleanup_row.connect_selected_notify(move |row| {
        let value = match row.selected() {
            1 => "always",
            2 => "never",
            _ => "ask",
        };
        settings_cleanup.set_string("update-cleanup", value).ok();
    });
    behavior_group.add(&cleanup_row);

    let auto_backup_row = adw::SwitchRow::builder()
        .title(&i18n("Auto-backup before update"))
        .subtitle(&i18n("Back up config and data files before updating an AppImage"))
        .active(settings.boolean("auto-backup-before-update"))
        .build();
    let settings_backup = settings.clone();
    auto_backup_row.connect_active_notify(move |row| {
        settings_backup.set_boolean("auto-backup-before-update", row.is_active()).ok();
    });
    behavior_group.add(&auto_backup_row);

    let retention_row = adw::SpinRow::builder()
        .title(&i18n("Backup retention"))
        .subtitle(&i18n("Days to keep config backups before auto-cleanup"))
        .build();
    let adjustment = gtk::Adjustment::new(
        settings.int("backup-retention-days") as f64,
        1.0,
        365.0,
        1.0,
        7.0,
        0.0,
    );
    retention_row.set_adjustment(Some(&adjustment));
    let settings_ret = settings.clone();
    retention_row.connect_value_notify(move |row| {
        settings_ret.set_int("backup-retention-days", row.value() as i32).ok();
    });
    behavior_group.add(&retention_row);

    page.add(&behavior_group);

    // Security Scanning group
    let security_group = adw::PreferencesGroup::builder()
        .title(&i18n("Security Scanning"))
        .description(&i18n("Automatically check the components bundled inside your apps for known security issues via the OSV.dev database."))
        .build();

    let auto_security_row = adw::SwitchRow::builder()
        .title(&i18n("Auto-scan new AppImages"))
        .subtitle(&i18n("Automatically run a security scan on newly discovered AppImages"))
        .active(settings.boolean("auto-security-scan"))
        .build();
    let settings_sec = settings.clone();
    auto_security_row.connect_active_notify(move |row| {
        settings_sec.set_boolean("auto-security-scan", row.is_active()).ok();
    });
    security_group.add(&auto_security_row);

    let notify_row = adw::SwitchRow::builder()
        .title(&i18n("Security notifications"))
        .subtitle(&i18n("Send desktop notifications when new CVEs are found"))
        .active(settings.boolean("security-notifications"))
        .build();
    let settings_notify = settings.clone();
    notify_row.connect_active_notify(move |row| {
        settings_notify.set_boolean("security-notifications", row.is_active()).ok();
    });
    security_group.add(&notify_row);

    let threshold_row = adw::ComboRow::builder()
        .title(&i18n("Notification threshold"))
        .subtitle(&i18n("Minimum severity to trigger a notification"))
        .build();
    let threshold_model = gtk::StringList::new(&[&i18n("Critical"), &i18n("High"), &i18n("Medium"), &i18n("Low")]);
    threshold_row.set_model(Some(&threshold_model));

    let current_threshold = settings.string("security-notification-threshold");
    threshold_row.set_selected(match current_threshold.as_str() {
        "critical" => 0,
        "high" => 1,
        "medium" => 2,
        "low" => 3,
        _ => 1,
    });

    let settings_threshold = settings.clone();
    threshold_row.connect_selected_notify(move |row| {
        let value = match row.selected() {
            0 => "critical",
            2 => "medium",
            3 => "low",
            _ => "high",
        };
        settings_threshold.set_string("security-notification-threshold", value).ok();
    });
    security_group.add(&threshold_row);

    page.add(&security_group);

    // Catalog Enrichment group
    let enrichment_group = adw::PreferencesGroup::builder()
        .title(&i18n("Catalog Enrichment"))
        .description(&i18n("Fetch additional app information like stars, downloads, and descriptions from GitHub to enrich the catalog."))
        .build();

    let auto_enrich_row = adw::SwitchRow::builder()
        .title(&i18n("Auto-enrich catalog apps"))
        .subtitle(&i18n("Fetch metadata from GitHub in the background"))
        .active(settings.boolean("catalog-auto-enrich"))
        .build();
    let settings_enrich = settings.clone();
    auto_enrich_row.connect_active_notify(move |row| {
        settings_enrich.set_boolean("catalog-auto-enrich", row.is_active()).ok();
    });
    enrichment_group.add(&auto_enrich_row);

    let token_row = adw::PasswordEntryRow::builder()
        .title(&i18n("GitHub token"))
        .build();
    let current_token = settings.string("github-token");
    if !current_token.is_empty() {
        token_row.set_text(&current_token);
    }
    let settings_token = settings.clone();
    token_row.connect_changed(move |row| {
        settings_token.set_string("github-token", &row.text()).ok();
    });
    enrichment_group.add(&token_row);

    let token_hint = adw::ActionRow::builder()
        .title(&i18n("Optional. Speeds up catalog data fetching. Get a free token at github.com/settings/tokens (no special permissions needed)."))
        .css_classes(["dim-label"])
        .build();
    enrichment_group.add(&token_hint);

    page.add(&enrichment_group);

    page
}

fn add_directory_row(list_box: &gtk::ListBox, dir: &str, settings: &gio::Settings) {
    let row = adw::ActionRow::builder()
        .title(dir)
        .build();

    let remove_label = format!("{} {}", i18n("Remove directory"), dir);
    let remove_btn = super::widgets::accessible_icon_button(
        "edit-delete-symbolic",
        &remove_label,
        &i18n("Remove"),
    );

    let list_ref = list_box.clone();
    let settings_ref = settings.clone();
    let dir_str = dir.to_string();
    let row_ref = row.clone();
    remove_btn.connect_clicked(move |_| {
        let current_dirs: Vec<String> = settings_ref
            .strv("scan-directories")
            .iter()
            .map(|s| s.to_string())
            .filter(|s| s != &dir_str)
            .collect();
        let refs: Vec<&str> = current_dirs.iter().map(|s| s.as_str()).collect();
        settings_ref.set_strv("scan-directories", refs).ok();

        list_ref.remove(&row_ref);
    });

    row.add_suffix(&remove_btn);
    list_box.append(&row);
}
