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

use crate::config::APP_ID;
use crate::core::database::Database;
use crate::core::notification;
use crate::core::report;
use crate::core::security;
use crate::i18n::{i18n, i18n_f};
use super::widgets;

/// Build the security scan report as a full navigation page.
pub fn build_security_report_page(db: &Rc<Database>) -> adw::NavigationPage {
    // We use a stack to swap content after scan completes
    let content_stack = gtk::Stack::new();
    content_stack.set_transition_type(gtk::StackTransitionType::Crossfade);

    // Build initial content
    let initial_content = build_report_content(db);
    content_stack.add_named(&initial_content, Some("content"));

    // Header bar with scan and export buttons
    let header = adw::HeaderBar::new();

    // Export button
    let export_button = gtk::Button::builder()
        .label("Export")
        .tooltip_text("Save this report as HTML, JSON, or CSV")
        .build();
    export_button.update_property(&[
        gtk::accessible::Property::Label("Export security report as HTML, JSON, or CSV"),
    ]);

    // Scan button
    let scan_button = gtk::Button::builder()
        .label("Scan All")
        .tooltip_text("Scan all AppImages for vulnerabilities")
        .build();
    scan_button.add_css_class("suggested-action");
    scan_button.update_property(&[
        gtk::accessible::Property::Label("Scan all AppImages for vulnerabilities"),
    ]);

    let db_scan = db.clone();
    let stack_ref = content_stack.clone();
    scan_button.connect_clicked(move |btn| {
        btn.set_sensitive(false);
        btn.set_label("Scanning...");
        widgets::announce(btn.upcast_ref::<gtk::Widget>(), "Scanning for vulnerabilities");
        let btn_clone = btn.clone();
        let db_refresh = db_scan.clone();
        let stack_refresh = stack_ref.clone();
        glib::spawn_future_local(async move {
            let result = gio::spawn_blocking(move || {
                let bg_db = Database::open().expect("Failed to open database");
                security::batch_scan(&bg_db)
            })
            .await;

            btn_clone.set_sensitive(true);
            btn_clone.set_label("Scan All");

            if let Ok(results) = result {
                let total_cves: usize = results.iter().map(|r| r.total_cves()).sum();
                for r in &results {
                    log::info!(
                        "Security scan: appimage_id={} found {} CVEs",
                        r.appimage_id, r.total_cves(),
                    );
                }
                log::info!("Security scan complete: {} CVEs found across {} AppImages", total_cves, results.len());
                widgets::announce(
                    &stack_refresh,
                    &format!("Security scan complete: {} vulnerabilities found", total_cves),
                );

                // Refresh the page content with updated data
                let new_content = build_report_content(&db_refresh);
                if let Some(old) = stack_refresh.child_by_name("content") {
                    stack_refresh.remove(&old);
                }
                stack_refresh.add_named(&new_content, Some("content"));
                stack_refresh.set_visible_child_name("content");

                // Send desktop notifications for new CVE findings if enabled
                let settings = gio::Settings::new(APP_ID);
                if settings.boolean("security-notifications") {
                    let threshold = settings.string("security-notification-threshold").to_string();
                    glib::spawn_future_local(async move {
                        let _ = gio::spawn_blocking(move || {
                            let bg_db = Database::open().expect("Failed to open database");
                            notification::check_and_notify(&bg_db, &threshold);
                        })
                        .await;
                    });
                }
            }
        });
    });
    header.pack_end(&scan_button);
    header.pack_end(&export_button);

    // Toast overlay wraps the toolbar for feedback
    let toast_overlay = adw::ToastOverlay::new();

    let toolbar = adw::ToolbarView::new();
    toolbar.add_top_bar(&header);
    toolbar.set_content(Some(&content_stack));
    toast_overlay.set_child(Some(&toolbar));

    // Wire export button
    let db_export = db.clone();
    let toast_ref = toast_overlay.clone();
    export_button.connect_clicked(move |btn| {
        let html_filter = gtk::FileFilter::new();
        html_filter.set_name(Some("HTML (*.html)"));
        html_filter.add_pattern("*.html");
        html_filter.add_pattern("*.htm");

        let json_filter = gtk::FileFilter::new();
        json_filter.set_name(Some("JSON (*.json)"));
        json_filter.add_pattern("*.json");

        let csv_filter = gtk::FileFilter::new();
        csv_filter.set_name(Some("CSV (*.csv)"));
        csv_filter.add_pattern("*.csv");

        let filters = gio::ListStore::new::<gtk::FileFilter>();
        filters.append(&html_filter);
        filters.append(&json_filter);
        filters.append(&csv_filter);

        let default_format = report::ReportFormat::Html;
        let initial_name = format!(
            "driftwood-security-report.{}",
            default_format.extension(),
        );
        let dialog = gtk::FileDialog::builder()
            .title("Export Security Report")
            .initial_name(&initial_name)
            .filters(&filters)
            .default_filter(&html_filter)
            .modal(true)
            .build();

        let btn_clone = btn.clone();
        let db_for_save = db_export.clone();
        let toast_for_save = toast_ref.clone();
        let window = btn.root().and_downcast::<gtk::Window>();

        dialog.save(window.as_ref(), None::<&gio::Cancellable>, move |result| {
            let Ok(file) = result else { return };
            let Some(path) = file.path() else { return };

            // Detect format from extension
            let ext = path.extension()
                .and_then(|e| e.to_str())
                .unwrap_or("html")
                .to_lowercase();

            let format = report::ReportFormat::from_str(&ext)
                .unwrap_or(report::ReportFormat::Html);

            btn_clone.set_sensitive(false);
            btn_clone.set_label("Exporting...");
            widgets::announce(btn_clone.upcast_ref::<gtk::Widget>(), "Exporting report");
            let btn_done = btn_clone.clone();
            let toast_done = toast_for_save.clone();
            let db_bg = db_for_save.clone();

            glib::spawn_future_local(async move {
                let path_clone = path.clone();
                let result = gio::spawn_blocking(move || {
                    let bg_db = Database::open().expect("Failed to open database");
                    // Use the records from the bg_db since Rc<Database> isn't Send
                    let _ = db_bg; // acknowledge capture but use bg_db
                    let report_data = report::build_report(&bg_db, None);
                    let content = report::render(&report_data, format);
                    std::fs::write(&path_clone, content)
                })
                .await;

                btn_done.set_sensitive(true);
                btn_done.set_label("Export");

                match result {
                    Ok(Ok(())) => {
                        let filename = path.file_name()
                            .and_then(|n| n.to_str())
                            .unwrap_or("report");
                        toast_done.add_toast(
                            widgets::info_toast(&i18n_f("Report saved as {filename}", &[("{filename}", filename)])),
                        );
                    }
                    _ => {
                        toast_done.add_toast(widgets::error_toast(&i18n("Failed to export report")));
                    }
                }
            });
        });
    });

    adw::NavigationPage::builder()
        .title("Security Report")
        .tag("security-report")
        .child(&toast_overlay)
        .build()
}

fn build_report_content(db: &Rc<Database>) -> gtk::ScrolledWindow {
    let clamp = adw::Clamp::builder()
        .maximum_size(800)
        .tightening_threshold(600)
        .build();

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

    let summary = db.get_all_cve_summary().unwrap_or_default();

    if summary.total() == 0 {
        let has_libs = db.get_all_appimages()
            .unwrap_or_default()
            .iter()
            .any(|r| {
                db.get_bundled_libraries(r.id)
                    .map(|libs| !libs.is_empty())
                    .unwrap_or(false)
            });

        if !has_libs {
            let empty = adw::StatusPage::builder()
                .icon_name("security-medium-symbolic")
                .title("No Security Scans")
                .description("Run a security check to look for known issues in your apps.")
                .build();
            content.append(&empty);
        } else {
            let clean = adw::StatusPage::builder()
                .icon_name("security-high-symbolic")
                .title("All Clear")
                .description("No known security issues found in any of your apps.")
                .build();
            content.append(&clean);
        }
    } else {
        content.append(&build_summary_group(&summary));

        let records = db.get_all_appimages().unwrap_or_default();
        for record in &records {
            let app_summary = db.get_cve_summary(record.id).unwrap_or_default();
            if app_summary.total() == 0 {
                continue;
            }

            let cve_matches = db.get_cve_matches(record.id).unwrap_or_default();
            if cve_matches.is_empty() {
                continue;
            }

            let name = record.app_name.as_deref().unwrap_or(&record.filename);
            content.append(&build_app_findings_group(name, record.id, &app_summary, &cve_matches));
        }
    }

    clamp.set_child(Some(&content));
    gtk::ScrolledWindow::builder()
        .child(&clamp)
        .vexpand(true)
        .build()
}

fn build_summary_group(summary: &crate::core::database::CveSummary) -> adw::PreferencesGroup {
    let group = adw::PreferencesGroup::builder()
        .title("Vulnerability Summary")
        .description("Driftwood checks the software components bundled inside your apps against a database of known security issues (CVEs). Most issues are in underlying libraries, not the apps themselves.")
        .build();

    let total_row = adw::ActionRow::builder()
        .title("Total vulnerabilities")
        .subtitle(&summary.total().to_string())
        .tooltip_text("Known security issues found in the components bundled inside your apps")
        .build();
    let total_badge = widgets::status_badge(summary.max_severity(), summary.badge_class());
    total_badge.set_valign(gtk::Align::Center);
    total_row.add_suffix(&total_badge);
    group.add(&total_row);

    if summary.critical > 0 {
        let row = adw::ActionRow::builder()
            .title("Critical")
            .subtitle(&summary.critical.to_string())
            .tooltip_text("Could allow an attacker to take control of affected components")
            .build();
        let badge = widgets::status_badge("Critical", "error");
        badge.set_valign(gtk::Align::Center);
        row.add_suffix(&badge);
        group.add(&row);
    }
    if summary.high > 0 {
        let row = adw::ActionRow::builder()
            .title("High")
            .subtitle(&summary.high.to_string())
            .tooltip_text("Could allow unauthorized access to data processed by the app")
            .build();
        let badge = widgets::status_badge("High", "error");
        badge.set_valign(gtk::Align::Center);
        row.add_suffix(&badge);
        group.add(&row);
    }
    if summary.medium > 0 {
        let row = adw::ActionRow::builder()
            .title("Medium")
            .subtitle(&summary.medium.to_string())
            .tooltip_text("Could cause the app to behave unexpectedly or crash")
            .build();
        let badge = widgets::status_badge("Medium", "warning");
        badge.set_valign(gtk::Align::Center);
        row.add_suffix(&badge);
        group.add(&row);
    }
    if summary.low > 0 {
        let row = adw::ActionRow::builder()
            .title("Low")
            .subtitle(&summary.low.to_string())
            .tooltip_text("Minor issue with limited practical impact")
            .build();
        let badge = widgets::status_badge("Low", "neutral");
        badge.set_valign(gtk::Align::Center);
        row.add_suffix(&badge);
        group.add(&row);
    }

    group
}

fn build_app_findings_group(
    app_name: &str,
    _record_id: i64,
    summary: &crate::core::database::CveSummary,
    cve_matches: &[crate::core::database::CveMatchRecord],
) -> adw::PreferencesGroup {
    let description = format!(
        "{} known security issues found. Check if a newer version is available in the catalog or from the developer's website. Most security issues are fixed in newer releases.",
        summary.total()
    );
    let group = adw::PreferencesGroup::builder()
        .title(app_name)
        .description(&description)
        .build();

    // Group CVEs by library
    let mut by_library: std::collections::BTreeMap<String, Vec<&crate::core::database::CveMatchRecord>> =
        std::collections::BTreeMap::new();

    for cve in cve_matches {
        let lib_name = cve.library_name.as_deref()
            .unwrap_or(&cve.library_soname);
        by_library
            .entry(lib_name.to_string())
            .or_default()
            .push(cve);
    }

    for (lib_name, cves) in &by_library {
        let lib_version = cves.first()
            .and_then(|c| c.library_version.as_deref())
            .unwrap_or("unknown");

        let expander = adw::ExpanderRow::builder()
            .title(&format!("{} (v{})", lib_name, lib_version))
            .subtitle(&format!("{} vulnerabilities", cves.len()))
            .build();

        let max_sev = cves.iter()
            .map(|c| c.severity.as_deref().unwrap_or("LOW"))
            .max_by_key(|s| match *s {
                "CRITICAL" => 4,
                "HIGH" => 3,
                "MEDIUM" => 2,
                "LOW" => 1,
                _ => 0,
            })
            .unwrap_or("LOW");

        let sev_class = match max_sev {
            "CRITICAL" | "HIGH" => "error",
            "MEDIUM" => "warning",
            _ => "neutral",
        };
        let lib_badge = widgets::status_badge(max_sev, sev_class);
        lib_badge.set_valign(gtk::Align::Center);
        expander.add_suffix(&lib_badge);

        for cve in cves {
            let severity = cve.severity.as_deref().unwrap_or("UNKNOWN");
            let summary_text = cve.summary.as_deref().unwrap_or("No description available");
            let subtitle = if let Some(ref fixed) = cve.fixed_version {
                format!("{} - Fixed in {}", summary_text, fixed)
            } else {
                summary_text.to_string()
            };

            let cve_row = adw::ActionRow::builder()
                .title(&format!("{} ({})", cve.cve_id, severity))
                .subtitle(&subtitle)
                .subtitle_selectable(true)
                .build();
            cve_row.update_property(&[
                gtk::accessible::Property::Description(&format!(
                    "{} severity vulnerability in {}. {}",
                    severity, lib_name, subtitle,
                )),
            ]);
            expander.add_row(&cve_row);
        }

        group.add(&expander);
    }

    group
}
