use std::path::Path;
use std::process::Command;

#[derive(Debug, Clone, PartialEq)]
pub enum WaylandStatus {
    /// Native Wayland support detected (GTK4, Qt6+Wayland, Electron 38+)
    Native,
    /// Will run under XWayland compatibility layer
    XWayland,
    /// Toolkit supports Wayland but plugins may be missing or env vars needed
    Possible,
    /// X11-only toolkit with no Wayland path (GTK2, old Electron, Java)
    X11Only,
    /// Could not determine (uncommon toolkit, static binary, etc.)
    Unknown,
}

impl WaylandStatus {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Native => "native",
            Self::XWayland => "xwayland",
            Self::Possible => "possible",
            Self::X11Only => "x11_only",
            Self::Unknown => "unknown",
        }
    }

    pub fn from_str(s: &str) -> Self {
        match s {
            "native" => Self::Native,
            "xwayland" => Self::XWayland,
            "possible" => Self::Possible,
            "x11_only" => Self::X11Only,
            _ => Self::Unknown,
        }
    }

    pub fn label(&self) -> &'static str {
        match self {
            Self::Native => "Native Wayland",
            Self::XWayland => "XWayland",
            Self::Possible => "Wayland possible",
            Self::X11Only => "X11 only",
            Self::Unknown => "Unknown",
        }
    }

    pub fn badge_class(&self) -> &'static str {
        match self {
            Self::Native => "success",
            Self::XWayland | Self::Possible => "warning",
            Self::X11Only => "error",
            Self::Unknown => "neutral",
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum DetectedToolkit {
    Gtk4,
    Gtk3Wayland,
    Gtk3X11Only,
    Gtk2,
    Qt6Wayland,
    Qt6X11Only,
    Qt5Wayland,
    Qt5X11Only,
    ElectronNative(u32),     // version >= 38
    ElectronFlagged(u32),    // version 28-37 with ozone flag
    ElectronLegacy(u32),     // version < 28
    JavaSwing,
    Flutter,
    Unknown,
}

impl DetectedToolkit {
    pub fn label(&self) -> String {
        match self {
            Self::Gtk4 => "GTK4".to_string(),
            Self::Gtk3Wayland => "GTK3 (Wayland)".to_string(),
            Self::Gtk3X11Only => "GTK3 (X11)".to_string(),
            Self::Gtk2 => "GTK2".to_string(),
            Self::Qt6Wayland => "Qt6 (Wayland)".to_string(),
            Self::Qt6X11Only => "Qt6 (X11)".to_string(),
            Self::Qt5Wayland => "Qt5 (Wayland)".to_string(),
            Self::Qt5X11Only => "Qt5 (X11)".to_string(),
            Self::ElectronNative(v) => format!("Electron {} (native Wayland)", v),
            Self::ElectronFlagged(v) => format!("Electron {} (Wayland with flags)", v),
            Self::ElectronLegacy(v) => format!("Electron {} (X11)", v),
            Self::JavaSwing => "Java/Swing".to_string(),
            Self::Flutter => "Flutter".to_string(),
            Self::Unknown => "Unknown".to_string(),
        }
    }

    pub fn wayland_status(&self) -> WaylandStatus {
        match self {
            Self::Gtk4 | Self::Gtk3Wayland | Self::Qt6Wayland | Self::Qt5Wayland
            | Self::ElectronNative(_) | Self::Flutter => WaylandStatus::Native,
            Self::ElectronFlagged(_) => WaylandStatus::Possible,
            Self::Gtk3X11Only | Self::Qt6X11Only | Self::Qt5X11Only => WaylandStatus::XWayland,
            Self::Gtk2 | Self::ElectronLegacy(_) | Self::JavaSwing => WaylandStatus::X11Only,
            Self::Unknown => WaylandStatus::Unknown,
        }
    }
}

#[derive(Debug, Clone)]
pub struct WaylandAnalysis {
    pub status: WaylandStatus,
    pub toolkit: DetectedToolkit,
    pub libraries_found: Vec<String>,
}

/// Analyze an AppImage's Wayland compatibility by inspecting its bundled libraries.
/// Uses unsquashfs to list files inside the squashfs.
pub fn analyze_appimage(appimage_path: &Path) -> WaylandAnalysis {
    let libs = list_bundled_libraries(appimage_path);

    let toolkit = detect_toolkit(&libs);
    let status = toolkit.wayland_status();

    WaylandAnalysis {
        status,
        toolkit,
        libraries_found: libs,
    }
}

/// List shared libraries bundled inside the AppImage squashfs.
fn list_bundled_libraries(appimage_path: &Path) -> Vec<String> {
    // Get the squashfs offset using binary scan (never execute the AppImage -
    // some apps like Affinity have custom AppRun scripts that ignore flags)
    let offset = match crate::core::inspector::find_squashfs_offset_for(appimage_path) {
        Some(o) => o.to_string(),
        None => return Vec::new(),
    };

    // Use unsquashfs to list files (just filenames, no extraction)
    let output = Command::new("unsquashfs")
        .args(["-o", &offset, "-l", "-no-progress"])
        .arg(appimage_path)
        .output();

    match output {
        Ok(out) if out.status.success() => {
            let stdout = String::from_utf8_lossy(&out.stdout);
            stdout
                .lines()
                .filter(|line| line.contains(".so"))
                .map(|line| {
                    // unsquashfs -l output format: "squashfs-root/usr/lib/libfoo.so.1"
                    // Extract just the filename
                    line.rsplit('/').next().unwrap_or(line).to_string()
                })
                .collect()
        }
        _ => Vec::new(),
    }
}

/// Detect the UI toolkit from the bundled library list.
fn detect_toolkit(libs: &[String]) -> DetectedToolkit {
    let has = |pattern: &str| -> bool {
        libs.iter().any(|l| l.contains(pattern))
    };

    // Check for GTK4 (always Wayland-native)
    if has("libgtk-4") || has("libGdk-4") {
        return DetectedToolkit::Gtk4;
    }

    // Check for Flutter (GTK backend, Wayland-native)
    if has("libflutter_linux_gtk") {
        return DetectedToolkit::Flutter;
    }

    // Check for Java/Swing (X11 only)
    if has("libjvm.so") || has("libjava.so") || has("libawt.so") {
        return DetectedToolkit::JavaSwing;
    }

    // Check for Electron (version-dependent)
    if has("libElectron") || has("electron") || has("libnode.so") || has("libchromium") {
        let version = detect_electron_version(libs);
        if let Some(v) = version {
            if v >= 38 {
                return DetectedToolkit::ElectronNative(v);
            } else if v >= 28 {
                return DetectedToolkit::ElectronFlagged(v);
            } else {
                return DetectedToolkit::ElectronLegacy(v);
            }
        }
        // Can't determine version - assume modern enough for XWayland at minimum
        return DetectedToolkit::ElectronFlagged(0);
    }

    // Check for Qt6
    if has("libQt6Core") || has("libQt6Gui") {
        if has("libQt6WaylandClient") || has("libqwayland") {
            return DetectedToolkit::Qt6Wayland;
        }
        return DetectedToolkit::Qt6X11Only;
    }

    // Check for Qt5
    if has("libQt5Core") || has("libQt5Gui") {
        if has("libQt5WaylandClient") || has("libqwayland") {
            return DetectedToolkit::Qt5Wayland;
        }
        return DetectedToolkit::Qt5X11Only;
    }

    // Check for GTK3
    if has("libgtk-3") || has("libGdk-3") {
        if has("libwayland-client") {
            return DetectedToolkit::Gtk3Wayland;
        }
        return DetectedToolkit::Gtk3X11Only;
    }

    // Check for GTK2 (X11 only, forever)
    if has("libgtk-x11-2") || has("libgdk-x11-2") {
        return DetectedToolkit::Gtk2;
    }

    DetectedToolkit::Unknown
}

/// Try to detect Electron version from bundled files.
fn detect_electron_version(libs: &[String]) -> Option<u32> {
    for lib in libs {
        // Look for version patterns in Electron-related files
        if lib.contains("electron") {
            // Try to extract version number from filenames like "electron-v28.0.0"
            for part in lib.split(&['-', '_', 'v'][..]) {
                if let Some(major) = part.split('.').next() {
                    if let Ok(v) = major.parse::<u32>() {
                        if v > 0 && v < 200 {
                            return Some(v);
                        }
                    }
                }
            }
        }
    }
    None
}

/// Detect the current desktop session type.
#[derive(Debug, Clone, PartialEq)]
pub enum SessionType {
    Wayland,
    X11,
    Unknown,
}

impl SessionType {
    pub fn label(&self) -> &'static str {
        match self {
            Self::Wayland => "Wayland",
            Self::X11 => "X11",
            Self::Unknown => "Unknown",
        }
    }
}

pub fn detect_session_type() -> SessionType {
    // Check XDG_SESSION_TYPE first (most reliable)
    if let Ok(session) = std::env::var("XDG_SESSION_TYPE") {
        return match session.as_str() {
            "wayland" => SessionType::Wayland,
            "x11" => SessionType::X11,
            _ => SessionType::Unknown,
        };
    }

    // Check WAYLAND_DISPLAY
    if std::env::var("WAYLAND_DISPLAY").is_ok() {
        return SessionType::Wayland;
    }

    // Check DISPLAY (X11 fallback)
    if std::env::var("DISPLAY").is_ok() {
        return SessionType::X11;
    }

    SessionType::Unknown
}

/// Get desktop environment info string.
pub fn detect_desktop_environment() -> String {
    let de = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
    let session = std::env::var("DESKTOP_SESSION").unwrap_or_default();

    if !de.is_empty() {
        de
    } else if !session.is_empty() {
        session
    } else {
        "Unknown".to_string()
    }
}

/// Result of analyzing a running process for Wayland usage.
#[derive(Debug, Clone)]
pub struct RuntimeAnalysis {
    pub pid: u32,
    pub has_wayland_socket: bool,
    pub has_x11_connection: bool,
    pub env_vars: Vec<(String, String)>,
}

impl RuntimeAnalysis {
    /// Human-readable status label.
    pub fn status_label(&self) -> &'static str {
        match (self.has_wayland_socket, self.has_x11_connection) {
            (true, false) => "Native Wayland",
            (true, true) => "Wayland + X11 fallback",
            (false, true) => "X11 / XWayland",
            (false, false) => "Unknown",
        }
    }

    /// Machine-readable status string for database storage.
    pub fn as_status_str(&self) -> &'static str {
        match (self.has_wayland_socket, self.has_x11_connection) {
            (true, false) => "native",
            (true, true) => "native",
            (false, true) => "xwayland",
            (false, false) => "unknown",
        }
    }
}

/// Analyze a running process to determine its actual Wayland/X11 usage.
/// Inspects /proc/<pid>/fd for Wayland and X11 sockets, and reads
/// relevant environment variables from /proc/<pid>/environ.
pub fn analyze_running_process(pid: u32) -> Result<RuntimeAnalysis, String> {
    let proc_path = format!("/proc/{}", pid);
    if !std::path::Path::new(&proc_path).exists() {
        return Err(format!("Process {} not found", pid));
    }

    // Check file descriptors for Wayland and X11 sockets
    let fd_dir = format!("{}/fd", proc_path);
    let mut has_wayland_socket = false;
    let mut has_x11_connection = false;

    if let Ok(entries) = std::fs::read_dir(&fd_dir) {
        for entry in entries.flatten() {
            if let Ok(target) = std::fs::read_link(entry.path()) {
                let target_str = target.to_string_lossy();
                if target_str.contains("wayland") {
                    has_wayland_socket = true;
                }
                if target_str.contains("/tmp/.X11-unix/") || target_str.contains("@/tmp/.X11") {
                    has_x11_connection = true;
                }
            }
        }
    }

    // Read relevant environment variables
    let environ_path = format!("{}/environ", proc_path);
    let mut env_vars = Vec::new();

    let relevant_vars = [
        "WAYLAND_DISPLAY", "DISPLAY", "GDK_BACKEND", "QT_QPA_PLATFORM",
        "XDG_SESSION_TYPE", "SDL_VIDEODRIVER", "CLUTTER_BACKEND",
    ];

    if let Ok(data) = std::fs::read(&environ_path) {
        for entry in data.split(|&b| b == 0) {
            if let Ok(s) = std::str::from_utf8(entry) {
                if let Some((key, value)) = s.split_once('=') {
                    if relevant_vars.contains(&key) {
                        env_vars.push((key.to_string(), value.to_string()));
                    }
                }
            }
        }
    }

    // Also check env vars for hints if fd inspection was inconclusive
    if !has_wayland_socket {
        has_wayland_socket = env_vars.iter().any(|(k, v)| {
            (k == "GDK_BACKEND" && v.contains("wayland"))
                || (k == "QT_QPA_PLATFORM" && v.contains("wayland"))
        });
    }

    Ok(RuntimeAnalysis {
        pid,
        has_wayland_socket,
        has_x11_connection,
        env_vars,
    })
}

/// Check if XWayland is available on the system.
pub fn has_xwayland() -> bool {
    // Check if Xwayland process is running
    Command::new("pgrep")
        .arg("Xwayland")
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_wayland_status_roundtrip() {
        let statuses = [
            WaylandStatus::Native,
            WaylandStatus::XWayland,
            WaylandStatus::Possible,
            WaylandStatus::X11Only,
            WaylandStatus::Unknown,
        ];
        for status in &statuses {
            assert_eq!(&WaylandStatus::from_str(status.as_str()), status);
        }
    }

    #[test]
    fn test_detect_toolkit_gtk4() {
        let libs = vec!["libgtk-4.so.1".to_string(), "libglib-2.0.so.0".to_string()];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Gtk4));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::Native);
    }

    #[test]
    fn test_detect_toolkit_qt5_wayland() {
        let libs = vec![
            "libQt5Core.so.5".to_string(),
            "libQt5Gui.so.5".to_string(),
            "libQt5WaylandClient.so.5".to_string(),
        ];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Qt5Wayland));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::Native);
    }

    #[test]
    fn test_detect_toolkit_qt5_x11() {
        let libs = vec![
            "libQt5Core.so.5".to_string(),
            "libQt5Gui.so.5".to_string(),
        ];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Qt5X11Only));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::XWayland);
    }

    #[test]
    fn test_detect_toolkit_gtk2() {
        let libs = vec!["libgtk-x11-2.0.so.0".to_string()];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Gtk2));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::X11Only);
    }

    #[test]
    fn test_detect_toolkit_gtk3_with_wayland() {
        let libs = vec![
            "libgtk-3.so.0".to_string(),
            "libwayland-client.so.0".to_string(),
        ];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Gtk3Wayland));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::Native);
    }

    #[test]
    fn test_detect_toolkit_unknown() {
        let libs = vec!["libfoo.so.1".to_string()];
        let toolkit = detect_toolkit(&libs);
        assert!(matches!(toolkit, DetectedToolkit::Unknown));
        assert_eq!(toolkit.wayland_status(), WaylandStatus::Unknown);
    }

    #[test]
    fn test_badge_classes() {
        assert_eq!(WaylandStatus::Native.badge_class(), "success");
        assert_eq!(WaylandStatus::XWayland.badge_class(), "warning");
        assert_eq!(WaylandStatus::X11Only.badge_class(), "error");
        assert_eq!(WaylandStatus::Unknown.badge_class(), "neutral");
    }
}
