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

use crate::core::database::Database;
use crate::core::duplicates;
use crate::core::footprint;
use crate::core::orphan;
use crate::i18n::{i18n, i18n_f};
use super::widgets;

/// A reclaimable item discovered during analysis.
#[derive(Debug, Clone)]
struct ReclaimableItem {
    label: String,
    subtitle: String,
    path: String,
    size_bytes: u64,
    category: ReclaimCategory,
    selected: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum ReclaimCategory {
    OrphanedDesktopEntry,
    CacheData,
    DuplicateAppImage,
}

impl ReclaimCategory {
    fn label(&self) -> String {
        match self {
            ReclaimCategory::OrphanedDesktopEntry => i18n("Orphaned desktop entries"),
            ReclaimCategory::CacheData => i18n("Cache data"),
            ReclaimCategory::DuplicateAppImage => i18n("Duplicate AppImages"),
        }
    }

    fn icon_name(&self) -> &'static str {
        match self {
            ReclaimCategory::OrphanedDesktopEntry => "user-trash-symbolic",
            ReclaimCategory::CacheData => "folder-templates-symbolic",
            ReclaimCategory::DuplicateAppImage => "edit-copy-symbolic",
        }
    }
}

/// Show the disk space reclamation wizard as an AdwDialog.
pub fn show_cleanup_wizard(parent: &impl IsA<gtk::Widget>, _db: &Rc<Database>) {
    let dialog = adw::Dialog::builder()
        .title(&i18n("Disk Space Cleanup"))
        .content_width(500)
        .content_height(550)
        .build();

    let stack = gtk::Stack::new();
    stack.set_transition_type(gtk::StackTransitionType::SlideLeft);

    // Shared state
    let items: Rc<RefCell<Vec<ReclaimableItem>>> = Rc::new(RefCell::new(Vec::new()));

    // Step 1: Analysis (scanning) - use adw::Spinner
    let step1 = build_analysis_step();
    stack.add_named(&step1, Some("analysis"));

    // Step 2: Review placeholder
    let step2_placeholder = gtk::Box::new(gtk::Orientation::Vertical, 0);
    stack.add_named(&step2_placeholder, Some("review"));

    // Step 3: Complete placeholder
    let step3_placeholder = gtk::Box::new(gtk::Orientation::Vertical, 0);
    stack.add_named(&step3_placeholder, Some("complete"));

    // Header bar
    let header = adw::HeaderBar::new();
    let toolbar = adw::ToolbarView::new();
    toolbar.add_top_bar(&header);
    toolbar.set_content(Some(&stack));

    dialog.set_child(Some(&toolbar));
    dialog.present(Some(parent));

    // Start analysis in background
    let stack_ref = stack.clone();
    let items_ref = items.clone();
    let dialog_ref = dialog.clone();

    glib::spawn_future_local(async move {
        let result = gio::spawn_blocking(move || {
            let bg_db = Database::open().expect("Failed to open database");
            analyze_reclaimable(&bg_db)
        })
        .await;

        match result {
            Ok(found_items) => {
                *items_ref.borrow_mut() = found_items;

                let items_for_review = items_ref.clone();
                let stack_for_complete = stack_ref.clone();
                let dialog_for_complete = dialog_ref.clone();

                let item_count = items_ref.borrow().len();
                let review = build_review_step(
                    &items_for_review,
                    move |selected_items| {
                        let stack_c = stack_for_complete.clone();
                        let dialog_c = dialog_for_complete.clone();
                        execute_cleanup(selected_items, stack_c, dialog_c);
                    },
                );

                if let Some(child) = stack_ref.child_by_name("review") {
                    stack_ref.remove(&child);
                }
                stack_ref.add_named(&review, Some("review"));
                stack_ref.set_visible_child_name("review");
                widgets::announce(
                    stack_ref.upcast_ref::<gtk::Widget>(),
                    &format!("Analysis complete. Found {} reclaimable items.", item_count),
                );
            }
            Err(_) => {
                let error_page = adw::StatusPage::builder()
                    .icon_name("dialog-error-symbolic")
                    .title(&i18n("Analysis Failed"))
                    .description(&i18n("Could not analyze disk usage."))
                    .build();
                if let Some(child) = stack_ref.child_by_name("review") {
                    stack_ref.remove(&child);
                }
                stack_ref.add_named(&error_page, Some("review"));
                stack_ref.set_visible_child_name("review");
            }
        }
    });
}

fn build_analysis_step() -> gtk::Box {
    let page = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .valign(gtk::Align::Center)
        .halign(gtk::Align::Center)
        .spacing(24)
        .build();

    // Use adw::Spinner for a cleaner look
    let spinner = adw::Spinner::builder()
        .width_request(48)
        .height_request(48)
        .halign(gtk::Align::Center)
        .build();
    spinner.update_property(&[gtk::accessible::Property::Label("Analyzing disk usage")]);
    page.append(&spinner);

    let label = gtk::Label::builder()
        .label(&i18n("Analyzing disk usage..."))
        .css_classes(["title-3"])
        .build();
    page.append(&label);

    let subtitle = gtk::Label::builder()
        .label(&i18n("Checking for orphaned files, cache data, and duplicates"))
        .css_classes(["dimmed"])
        .build();
    page.append(&subtitle);

    page
}

fn build_review_step(
    items: &Rc<RefCell<Vec<ReclaimableItem>>>,
    on_confirm: impl Fn(Vec<ReclaimableItem>) + 'static,
) -> gtk::Box {
    let on_confirm: Rc<dyn Fn(Vec<ReclaimableItem>)> = Rc::new(on_confirm);

    let page = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .spacing(0)
        .build();

    let items_ref = items.borrow();

    if items_ref.is_empty() {
        let empty = adw::StatusPage::builder()
            .icon_name("emblem-ok-symbolic")
            .title(&i18n("All Clean"))
            .description(&i18n("No reclaimable disk space found."))
            .vexpand(true)
            .build();
        page.append(&empty);
        return page;
    }

    // Summary header
    let total_size: u64 = items_ref.iter().map(|i| i.size_bytes).sum();
    let summary_label = gtk::Label::builder()
        .label(&i18n_f("Found {} reclaimable", &[("{}", &widgets::format_size(total_size as i64))]))
        .css_classes(["title-3"])
        .margin_top(12)
        .margin_start(18)
        .margin_end(18)
        .halign(gtk::Align::Start)
        .build();
    page.append(&summary_label);

    let desc_label = gtk::Label::builder()
        .label(&i18n("Select items to remove"))
        .css_classes(["dimmed"])
        .margin_start(18)
        .margin_end(18)
        .margin_bottom(12)
        .halign(gtk::Align::Start)
        .build();
    page.append(&desc_label);

    // Scrollable list
    let scrolled = gtk::ScrolledWindow::builder()
        .vexpand(true)
        .build();

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

    let categories = [
        ReclaimCategory::DuplicateAppImage,
        ReclaimCategory::CacheData,
        ReclaimCategory::OrphanedDesktopEntry,
    ];

    let check_buttons: Rc<RefCell<Vec<(usize, gtk::CheckButton)>>> =
        Rc::new(RefCell::new(Vec::new()));

    for cat in &categories {
        let cat_items: Vec<(usize, &ReclaimableItem)> = items_ref
            .iter()
            .enumerate()
            .filter(|(_, i)| i.category == *cat)
            .collect();

        if cat_items.is_empty() {
            continue;
        }

        let cat_size: u64 = cat_items.iter().map(|(_, i)| i.size_bytes).sum();

        // Category header with icon
        let cat_header = gtk::Box::builder()
            .orientation(gtk::Orientation::Horizontal)
            .spacing(8)
            .build();
        let cat_icon = gtk::Image::from_icon_name(cat.icon_name());
        cat_icon.set_pixel_size(16);
        cat_icon.set_accessible_role(gtk::AccessibleRole::Presentation);
        cat_header.append(&cat_icon);
        let cat_label_text = cat.label();
        let cat_label = gtk::Label::builder()
            .label(&format!("{} ({})", cat_label_text, widgets::format_size(cat_size as i64)))
            .css_classes(["title-4"])
            .halign(gtk::Align::Start)
            .build();
        cat_header.append(&cat_label);
        content.append(&cat_header);

        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(&cat_label_text),
        ]);

        for (idx, item) in &cat_items {
            let check = gtk::CheckButton::builder()
                .active(item.selected)
                .valign(gtk::Align::Center)
                .build();
            check.update_property(&[gtk::accessible::Property::Label(&item.label)]);
            check_buttons.borrow_mut().push((*idx, check.clone()));

            let row = adw::ActionRow::builder()
                .title(&item.label)
                .subtitle(&item.subtitle)
                .activatable_widget(&check)
                .build();
            row.add_prefix(&check);

            let size_label = gtk::Label::new(Some(&widgets::format_size(item.size_bytes as i64)));
            size_label.add_css_class("dimmed");
            size_label.set_valign(gtk::Align::Center);
            row.add_suffix(&size_label);

            list_box.append(&row);
        }
        content.append(&list_box);
    }

    scrolled.set_child(Some(&content));
    page.append(&scrolled);

    // Bottom bar
    let bottom_bar = gtk::Box::builder()
        .orientation(gtk::Orientation::Horizontal)
        .spacing(12)
        .margin_top(12)
        .margin_bottom(12)
        .margin_start(18)
        .margin_end(18)
        .halign(gtk::Align::End)
        .build();

    let clean_button = gtk::Button::builder()
        .label(&i18n("Clean Selected"))
        .build();
    clean_button.add_css_class("destructive-action");
    clean_button.update_property(&[
        gtk::accessible::Property::Label(&i18n("Clean selected items")),
    ]);

    let items_clone = items.clone();
    let checks = check_buttons;
    let on_confirm_ref = on_confirm.clone();
    clean_button.connect_clicked(move |btn| {
        let checks = checks.borrow();
        let mut items_mut = items_clone.borrow_mut();
        for (idx, check) in checks.iter() {
            if *idx < items_mut.len() {
                items_mut[*idx].selected = check.is_active();
            }
        }
        let selected: Vec<ReclaimableItem> = items_mut
            .iter()
            .filter(|i| i.selected)
            .cloned()
            .collect();
        drop(items_mut);

        let count = selected.len();
        if count == 0 {
            on_confirm_ref(selected);
            return;
        }

        let confirm = adw::AlertDialog::builder()
            .heading(&i18n("Confirm Cleanup"))
            .body(&i18n_f(
                "Remove {} items?",
                &[("{}", &count.to_string())],
            ))
            .close_response("cancel")
            .default_response("clean")
            .build();
        confirm.add_response("cancel", &i18n("Cancel"));
        confirm.add_response("clean", &i18n("Clean"));
        confirm.set_response_appearance("clean", adw::ResponseAppearance::Destructive);

        let on_confirm_inner = on_confirm_ref.clone();
        confirm.connect_response(None, move |_, response| {
            if response == "clean" {
                on_confirm_inner(selected.clone());
            }
        });

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

    bottom_bar.append(&clean_button);
    page.append(&bottom_bar);

    page
}

fn execute_cleanup(
    items: Vec<ReclaimableItem>,
    stack: gtk::Stack,
    dialog: adw::Dialog,
) {
    let total_count = items.len();
    let total_size: u64 = items.iter().map(|i| i.size_bytes).sum();

    for item in &items {
        let path = std::path::Path::new(&item.path);
        if !path.exists() {
            continue;
        }
        match item.category {
            ReclaimCategory::OrphanedDesktopEntry => {
                std::fs::remove_file(path).ok();
            }
            ReclaimCategory::CacheData => {
                if path.is_dir() {
                    std::fs::remove_dir_all(path).ok();
                } else {
                    std::fs::remove_file(path).ok();
                }
            }
            ReclaimCategory::DuplicateAppImage => {
                std::fs::remove_file(path).ok();
            }
        }
    }

    let complete = build_complete_step(total_count, total_size, &dialog);
    if let Some(child) = stack.child_by_name("complete") {
        stack.remove(&child);
    }
    stack.add_named(&complete, Some("complete"));
    stack.set_visible_child_name("complete");
    if total_count > 0 {
        widgets::announce(
            stack.upcast_ref::<gtk::Widget>(),
            &format!("Cleanup complete. Removed {} items, freed {}.", total_count, widgets::format_size(total_size as i64)),
        );
    }
}

fn build_complete_step(count: usize, size: u64, dialog: &adw::Dialog) -> gtk::Box {
    let page = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .valign(gtk::Align::Center)
        .halign(gtk::Align::Center)
        .spacing(12)
        .vexpand(true)
        .build();

    if count == 0 {
        let status = adw::StatusPage::builder()
            .icon_name("emblem-ok-symbolic")
            .title(&i18n("Nothing Selected"))
            .description(&i18n("No items were selected for cleanup."))
            .build();
        page.append(&status);
    } else {
        let status = adw::StatusPage::builder()
            .icon_name("user-trash-symbolic")
            .title(&i18n("Cleanup Complete"))
            .description(&i18n_f(
                "Removed {count} items, freeing {size}",
                &[
                    ("{count}", &count.to_string()),
                    ("{size}", &widgets::format_size(size as i64)),
                ],
            ))
            .build();
        page.append(&status);
    }

    let close_button = gtk::Button::builder()
        .label(&i18n("Close"))
        .halign(gtk::Align::Center)
        .build();
    close_button.add_css_class("pill");
    close_button.update_property(&[
        gtk::accessible::Property::Label(&i18n("Close cleanup dialog")),
    ]);

    let dialog_ref = dialog.clone();
    close_button.connect_clicked(move |_| {
        dialog_ref.close();
    });
    page.append(&close_button);

    page
}

/// Analyze disk for reclaimable items.
fn analyze_reclaimable(db: &Database) -> Vec<ReclaimableItem> {
    let mut items = Vec::new();

    // 1. Orphaned desktop entries
    let orphans = orphan::detect_orphans();
    for entry in &orphans {
        let path_str = entry.desktop_file_path.to_string_lossy().to_string();
        let size = std::fs::metadata(&entry.desktop_file_path).map(|m| m.len()).unwrap_or(0);
        let name = entry.app_name.as_deref().unwrap_or("Unknown app");
        items.push(ReclaimableItem {
            label: format!("{} desktop entry", name),
            subtitle: path_str.clone(),
            path: path_str,
            size_bytes: size,
            category: ReclaimCategory::OrphanedDesktopEntry,
            selected: true,
        });
    }

    // 2. Cache data from footprint discovery
    let records = db.get_all_appimages().unwrap_or_default();
    for record in &records {
        let paths = db.get_app_data_paths(record.id).unwrap_or_default();
        for dp in &paths {
            if dp.path_type == "cache" {
                let path = std::path::Path::new(&dp.path);
                if path.exists() {
                    let size = footprint::dir_size_pub(path);
                    if size > 0 {
                        let app_name = record.app_name.as_deref().unwrap_or(&record.filename);
                        items.push(ReclaimableItem {
                            label: format!("{} cache", app_name),
                            subtitle: dp.path.clone(),
                            path: dp.path.clone(),
                            size_bytes: size,
                            category: ReclaimCategory::CacheData,
                            selected: true,
                        });
                    }
                }
            }
        }
    }

    // 3. Duplicate AppImages
    let dupes = duplicates::detect_duplicates(db);
    for group in &dupes {
        for member in &group.members {
            if member.recommendation == duplicates::MemberRecommendation::RemoveOlder
                || member.recommendation == duplicates::MemberRecommendation::RemoveDuplicate
            {
                items.push(ReclaimableItem {
                    label: member.record.filename.clone(),
                    subtitle: format!("Duplicate of {} - {}", group.app_name, member.record.path),
                    path: member.record.path.clone(),
                    size_bytes: member.record.size_bytes as u64,
                    category: ReclaimCategory::DuplicateAppImage,
                    selected: false, // Don't auto-select - user must explicitly choose
                });
            }
        }
    }

    items
}
