use std::path::PathBuf;
use std::sync::{Condvar, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};

use crate::core::database::Database;
use crate::core::discovery::AppImageType;
use crate::core::fuse;
use crate::core::inspector;
use crate::core::integrator;
use crate::core::wayland;

/// Maximum number of concurrent background analyses.
const MAX_CONCURRENT_ANALYSES: usize = 2;

/// Counter for currently running analyses.
static RUNNING_ANALYSES: AtomicUsize = AtomicUsize::new(0);

/// Condvar to efficiently wait for a slot instead of busy-polling.
static SLOT_AVAILABLE: (Mutex<()>, Condvar) = (Mutex::new(()), Condvar::new());

/// Returns the number of currently running background analyses.
pub fn running_count() -> usize {
    RUNNING_ANALYSES.load(Ordering::Relaxed)
}

/// RAII guard that decrements the analysis counter on drop and notifies waiters.
struct AnalysisGuard;

impl Drop for AnalysisGuard {
    fn drop(&mut self) {
        RUNNING_ANALYSES.fetch_sub(1, Ordering::Release);
        SLOT_AVAILABLE.1.notify_one();
    }
}

/// Run the heavy analysis steps for a single AppImage on a background thread.
///
/// This opens its own database connection and updates results as they complete.
/// All errors are logged but non-fatal - fields stay `None`, which the UI
/// already handles gracefully.
///
/// Blocks until a slot is available if the concurrency limit is reached.
pub fn run_background_analysis(id: i64, path: PathBuf, appimage_type: AppImageType, integrate: bool) {
    // Wait for a slot to become available using condvar instead of busy-polling
    loop {
        let current = RUNNING_ANALYSES.load(Ordering::Acquire);
        if current < MAX_CONCURRENT_ANALYSES {
            if RUNNING_ANALYSES.compare_exchange(current, current + 1, Ordering::AcqRel, Ordering::Relaxed).is_ok() {
                break;
            }
        } else {
            let lock = SLOT_AVAILABLE.0.lock().unwrap();
            let _ = SLOT_AVAILABLE.1.wait_timeout(lock, std::time::Duration::from_secs(1));
        }
    }
    let _guard = AnalysisGuard;

    let db = match Database::open() {
        Ok(db) => db,
        Err(e) => {
            log::error!("Background analysis: failed to open database: {}", e);
            return;
        }
    };

    if let Err(e) = db.update_analysis_status(id, "analyzing") {
        log::warn!("Failed to set analysis status to 'analyzing' for id {}: {}", id, e);
    }

    // Inspect metadata (app name, version, icon, desktop entry, AppStream, etc.)
    if let Ok(meta) = inspector::inspect_appimage(&path, &appimage_type) {
        log::debug!(
            "Metadata for id={}: name={:?}, icon_name={:?}",
            id, meta.app_name.as_deref(), meta.icon_name.as_deref(),
        );
        let categories = if meta.categories.is_empty() {
            None
        } else {
            Some(meta.categories.join(";"))
        };
        if let Err(e) = db.update_metadata(
            id,
            meta.app_name.as_deref(),
            meta.app_version.as_deref(),
            meta.description.as_deref(),
            meta.developer.as_deref(),
            categories.as_deref(),
            meta.architecture.as_deref(),
            meta.cached_icon_path
                .as_ref()
                .map(|p| p.to_string_lossy())
                .as_deref(),
            Some(&meta.desktop_entry_content),
        ) {
            log::warn!("Failed to update metadata for id {}: {}", id, e);
        }

        // Store extended metadata from AppStream XML and desktop entry
        let keywords = if meta.keywords.is_empty() {
            None
        } else {
            Some(meta.keywords.join(","))
        };
        let mime_types = if meta.mime_types.is_empty() {
            None
        } else {
            Some(meta.mime_types.join(";"))
        };
        let release_json = if meta.releases.is_empty() {
            None
        } else {
            let releases: Vec<serde_json::Value> = meta
                .releases
                .iter()
                .map(|r| {
                    serde_json::json!({
                        "version": r.version,
                        "date": r.date,
                        "description": r.description,
                    })
                })
                .collect();
            Some(serde_json::to_string(&releases).unwrap_or_default())
        };
        let actions_json = if meta.desktop_actions.is_empty() {
            None
        } else {
            Some(serde_json::to_string(&meta.desktop_actions).unwrap_or_default())
        };
        let screenshot_urls_str = if meta.screenshot_urls.is_empty() {
            None
        } else {
            Some(meta.screenshot_urls.join("\n"))
        };

        // Store StartupWMClass
        if let Some(ref wm_class) = meta.startup_wm_class {
            db.set_startup_wm_class(id, Some(wm_class)).ok();
        }

        if let Err(e) = db.update_appstream_metadata(
            id,
            meta.appstream_id.as_deref(),
            meta.appstream_description.as_deref(),
            meta.generic_name.as_deref(),
            meta.license.as_deref(),
            meta.homepage_url.as_deref(),
            meta.bugtracker_url.as_deref(),
            meta.donation_url.as_deref(),
            meta.help_url.as_deref(),
            meta.vcs_url.as_deref(),
            keywords.as_deref(),
            mime_types.as_deref(),
            meta.content_rating.as_deref(),
            meta.project_group.as_deref(),
            release_json.as_deref(),
            actions_json.as_deref(),
            meta.has_signature,
            screenshot_urls_str.as_deref(),
        ) {
            log::warn!("Failed to update appstream metadata for id {}: {}", id, e);
        }
    }

    // FUSE status
    let fuse_info = fuse::detect_system_fuse();
    let app_fuse = fuse::determine_app_fuse_status(&fuse_info, &path);
    if let Err(e) = db.update_fuse_status(id, app_fuse.as_str()) {
        log::warn!("Failed to update FUSE status for id {}: {}", id, e);
    }

    // Wayland status
    let analysis = wayland::analyze_appimage(&path);
    if let Err(e) = db.update_wayland_status(id, analysis.status.as_str()) {
        log::warn!("Failed to update Wayland status for id {}: {}", id, e);
    }

    // SHA256 hash
    if let Ok(hash) = crate::core::discovery::compute_sha256(&path) {
        if let Err(e) = db.update_sha256(id, &hash) {
            log::warn!("Failed to update SHA256 for id {}: {}", id, e);
        }
    }

    // Footprint discovery
    if let Ok(Some(rec)) = db.get_appimage_by_id(id) {
        crate::core::footprint::discover_and_store(&db, id, &rec);

        // Integrate if requested
        if integrate {
            match integrator::integrate_tracked(&rec, &db) {
                Ok(result) => {
                    let desktop_path = result.desktop_file_path.to_string_lossy().to_string();
                    if let Err(e) = db.set_integrated(id, true, Some(&desktop_path)) {
                        log::warn!("Failed to set integration status for id {}: {}", id, e);
                    }
                }
                Err(e) => {
                    log::error!("Integration failed for id {}: {}", id, e);
                }
            }
        }
    }

    if let Err(e) = db.update_analysis_status(id, "complete") {
        log::warn!("Failed to set analysis status to 'complete' for id {}: {}", id, e);
    }
    // _guard dropped here, decrementing RUNNING_ANALYSES
}
