use std::path::{Path, PathBuf};

use crate::error::Result;
use crate::preset::Preset;
use crate::storage::{atomic_write, PresetStore};

/// Supported file managers for right-click integration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileManager {
    Nautilus,
    Nemo,
    Thunar,
    Dolphin,
}

impl FileManager {
    /// Human-readable name.
    pub fn name(&self) -> &'static str {
        match self {
            Self::Nautilus => "Nautilus",
            Self::Nemo => "Nemo",
            Self::Thunar => "Thunar",
            Self::Dolphin => "Dolphin",
        }
    }

    /// Desktop file ID used to detect if the FM is installed.
    pub fn desktop_id(&self) -> &'static str {
        match self {
            Self::Nautilus => "org.gnome.Nautilus",
            Self::Nemo => "org.nemo.Nemo",
            Self::Thunar => "thunar",
            Self::Dolphin => "org.kde.dolphin",
        }
    }

    /// Install the integration extension for this file manager.
    pub fn install(&self) -> Result<()> {
        match self {
            Self::Nautilus => install_nautilus(),
            Self::Nemo => install_nemo(),
            Self::Thunar => install_thunar(),
            Self::Dolphin => install_dolphin(),
        }
    }

    /// Remove the integration extension for this file manager.
    pub fn uninstall(&self) -> Result<()> {
        match self {
            Self::Nautilus => uninstall_nautilus(),
            Self::Nemo => uninstall_nemo(),
            Self::Thunar => uninstall_thunar(),
            Self::Dolphin => uninstall_dolphin(),
        }
    }

    /// Check if the integration is currently installed.
    pub fn is_installed(&self) -> bool {
        match self {
            Self::Nautilus => nautilus_extension_path().exists(),
            Self::Nemo => nemo_action_path().exists(),
            Self::Thunar => thunar_action_path().exists(),
            Self::Dolphin => dolphin_service_path().exists(),
        }
    }
}

/// Regenerate the extension files for all installed file managers.
/// Call this after presets change so the submenu stays up to date.
pub fn regenerate_all() -> Result<()> {
    for fm in &[FileManager::Nautilus, FileManager::Nemo, FileManager::Thunar, FileManager::Dolphin] {
        if fm.is_installed() {
            fm.install()?;
        }
    }
    Ok(())
}

/// Sanitize a string for safe use in shell Exec= lines and XML command elements.
/// Removes or replaces characters that could cause shell injection.
fn shell_safe(s: &str) -> String {
    s.chars()
        .filter(|c| c.is_alphanumeric() || matches!(c, ' ' | '-' | '_' | '.' | '(' | ')' | ','))
        .collect()
}

fn pixstrip_bin() -> String {
    // Try to find the pixstrip binary path
    if let Ok(exe) = std::env::current_exe() {
        // If running from the GTK app, find the CLI sibling
        let dir = exe.parent().unwrap_or(Path::new("/usr/bin"));
        let cli_path = dir.join("pixstrip");
        if cli_path.exists() {
            return cli_path.display().to_string();
        }
        let gtk_path = dir.join("pixstrip-gtk");
        if gtk_path.exists() {
            return gtk_path.display().to_string();
        }
        return exe.display().to_string();
    }
    "pixstrip-gtk".into()
}

fn get_preset_names() -> Vec<String> {
    let mut names: Vec<String> = Preset::all_builtins()
        .into_iter()
        .map(|p| p.name)
        .collect();

    let store = PresetStore::new();
    if let Ok(user_presets) = store.list() {
        for p in user_presets {
            if p.is_custom && !names.contains(&p.name) {
                names.push(p.name);
            }
        }
    }
    names
}

// --- Nautilus (Python extension) ---

fn nautilus_extension_dir() -> PathBuf {
    let data = std::env::var("XDG_DATA_HOME")
        .unwrap_or_else(|_| {
            let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
            format!("{}/.local/share", home)
        });
    PathBuf::from(data).join("nautilus-python").join("extensions")
}

fn nautilus_extension_path() -> PathBuf {
    nautilus_extension_dir().join("pixstrip-integration.py")
}

fn install_nautilus() -> Result<()> {
    let dir = nautilus_extension_dir();
    std::fs::create_dir_all(&dir)?;

    let bin = pixstrip_bin();
    let presets = get_preset_names();

    let mut preset_items = String::new();
    for name in &presets {
        preset_items.push_str(&format!(
            "        item = Nautilus.MenuItem(\n\
             \x20           name='Pixstrip::Preset::{}',\n\
             \x20           label='{}',\n\
             \x20       )\n\
             \x20       item.connect('activate', self._on_preset, '{}', files)\n\
             \x20       submenu.append_item(item)\n\n",
            name.replace(' ', "_"),
            name.replace('\\', "\\\\").replace('\'', "\\'"),
            name.replace('\\', "\\\\").replace('\'', "\\'"),
        ));
    }

    let escaped_bin = bin.replace('\\', "\\\\").replace('\'', "\\'");
    let script = format!(
        r#"import subprocess
from gi.repository import Nautilus, GObject

class PixstripExtension(GObject.GObject, Nautilus.MenuProvider):
    def get_file_items(self, files):
        if not files:
            return []

        # Only show for image files
        image_mimes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif',
                       'image/tiff', 'image/avif', 'image/bmp']
        valid = [f for f in files if f.get_mime_type() in image_mimes or f.is_directory()]
        if not valid:
            return []

        top = Nautilus.MenuItem(
            name='Pixstrip::Menu',
            label='Process with Pixstrip',
            icon='applications-graphics-symbolic',
        )

        submenu = Nautilus.Menu()
        top.set_submenu(submenu)

        # Open in Pixstrip (wizard)
        open_item = Nautilus.MenuItem(
            name='Pixstrip::Open',
            label='Open in Pixstrip...',
        )
        open_item.connect('activate', self._on_open, files)
        submenu.append_item(open_item)

        # Separator via disabled item
        sep = Nautilus.MenuItem(
            name='Pixstrip::Sep',
            label='---',
            sensitive=False,
        )
        submenu.append_item(sep)

        # Preset items
{preset_items}
        return [top]

    def _on_open(self, menu, files):
        paths = [f.get_location().get_path() for f in files if f.get_location()]
        subprocess.Popen(['{bin}', '--files'] + paths)

    def _on_preset(self, menu, preset_name, files):
        paths = [f.get_location().get_path() for f in files if f.get_location()]
        subprocess.Popen(['{bin}', '--preset', preset_name, '--files'] + paths)
"#,
        preset_items = preset_items,
        bin = escaped_bin,
    );

    atomic_write(&nautilus_extension_path(), &script)?;
    Ok(())
}

fn uninstall_nautilus() -> Result<()> {
    let path = nautilus_extension_path();
    if path.exists() {
        std::fs::remove_file(path)?;
    }
    Ok(())
}

// --- Nemo (.nemo_action files) ---

fn nemo_action_dir() -> PathBuf {
    let data = std::env::var("XDG_DATA_HOME")
        .unwrap_or_else(|_| {
            let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
            format!("{}/.local/share", home)
        });
    PathBuf::from(data).join("nemo").join("actions")
}

fn nemo_action_path() -> PathBuf {
    nemo_action_dir().join("pixstrip-open.nemo_action")
}

fn install_nemo() -> Result<()> {
    let dir = nemo_action_dir();
    std::fs::create_dir_all(&dir)?;

    let bin = pixstrip_bin();

    // Main "Open in Pixstrip" action
    let open_action = format!(
        "[Nemo Action]\n\
         Name=Open in Pixstrip...\n\
         Comment=Process images with Pixstrip\n\
         Exec={bin} --files %F\n\
         Icon-Name=applications-graphics-symbolic\n\
         Selection=Any\n\
         Extensions=jpg;jpeg;png;webp;gif;tiff;tif;avif;bmp;\n\
         Mimetypes=image/*;\n",
        bin = bin,
    );
    atomic_write(&nemo_action_path(), &open_action)?;

    // Per-preset actions
    let presets = get_preset_names();
    for name in &presets {
        let safe_name = name.replace(' ', "-").to_lowercase();
        let action_path = dir.join(format!("pixstrip-preset-{}.nemo_action", safe_name));
        let action = format!(
            "[Nemo Action]\n\
             Name=Pixstrip: {name}\n\
             Comment=Process with {name} preset\n\
             Exec={bin} --preset \"{safe_label}\" --files %F\n\
             Icon-Name=applications-graphics-symbolic\n\
             Selection=Any\n\
             Extensions=jpg;jpeg;png;webp;gif;tiff;tif;avif;bmp;\n\
             Mimetypes=image/*;\n",
            name = name,
            safe_label = shell_safe(name),
            bin = bin,
        );
        atomic_write(&action_path, &action)?;
    }

    Ok(())
}

fn uninstall_nemo() -> Result<()> {
    let dir = nemo_action_dir();
    // Remove main action
    let main_path = nemo_action_path();
    if main_path.exists() {
        std::fs::remove_file(main_path)?;
    }
    // Remove preset actions
    if dir.exists() {
        for entry in std::fs::read_dir(&dir)?.flatten() {
            let name = entry.file_name();
            let name_str = name.to_string_lossy();
            if name_str.starts_with("pixstrip-preset-") && name_str.ends_with(".nemo_action") {
                let _ = std::fs::remove_file(entry.path());
            }
        }
    }
    Ok(())
}

// --- Thunar (Custom Actions via uca.xml) ---

fn thunar_action_dir() -> PathBuf {
    let config = std::env::var("XDG_CONFIG_HOME")
        .unwrap_or_else(|_| {
            let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
            format!("{}/.config", home)
        });
    PathBuf::from(config).join("Thunar")
}

fn thunar_action_path() -> PathBuf {
    thunar_action_dir().join("pixstrip-actions.xml")
}

fn install_thunar() -> Result<()> {
    let dir = thunar_action_dir();
    std::fs::create_dir_all(&dir)?;

    let bin = pixstrip_bin();
    let presets = get_preset_names();

    let mut actions = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actions>\n");

    // Open in Pixstrip
    actions.push_str(&format!(
        "  <action>\n\
         \x20   <icon>applications-graphics-symbolic</icon>\n\
         \x20   <name>Open in Pixstrip...</name>\n\
         \x20   <command>{bin} --files %F</command>\n\
         \x20   <description>Process images with Pixstrip</description>\n\
         \x20   <patterns>*.jpg;*.jpeg;*.png;*.webp;*.gif;*.tiff;*.tif;*.avif;*.bmp</patterns>\n\
         \x20   <image-files/>\n\
         \x20   <directories/>\n\
         </action>\n",
        bin = bin,
    ));

    for name in &presets {
        actions.push_str(&format!(
            "  <action>\n\
             \x20   <icon>applications-graphics-symbolic</icon>\n\
             \x20   <name>Pixstrip: {xml_name}</name>\n\
             \x20   <command>{bin} --preset \"{safe_label}\" --files %F</command>\n\
             \x20   <description>Process with {xml_name} preset</description>\n\
             \x20   <patterns>*.jpg;*.jpeg;*.png;*.webp;*.gif;*.tiff;*.tif;*.avif;*.bmp</patterns>\n\
             \x20   <image-files/>\n\
             \x20   <directories/>\n\
             </action>\n",
            xml_name = name.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;"),
            safe_label = shell_safe(name),
            bin = bin,
        ));
    }

    actions.push_str("</actions>\n");
    atomic_write(&thunar_action_path(), &actions)?;
    Ok(())
}

fn uninstall_thunar() -> Result<()> {
    let path = thunar_action_path();
    if path.exists() {
        std::fs::remove_file(path)?;
    }
    Ok(())
}

// --- Dolphin (KDE Service Menu) ---

fn dolphin_service_dir() -> PathBuf {
    let data = std::env::var("XDG_DATA_HOME")
        .unwrap_or_else(|_| {
            let home = std::env::var("HOME").unwrap_or_else(|_| dirs::home_dir().map(|h| h.to_string_lossy().into_owned()).unwrap_or_else(|| "/tmp".into()));
            format!("{}/.local/share", home)
        });
    PathBuf::from(data).join("kio").join("servicemenus")
}

fn dolphin_service_path() -> PathBuf {
    dolphin_service_dir().join("pixstrip.desktop")
}

fn install_dolphin() -> Result<()> {
    let dir = dolphin_service_dir();
    std::fs::create_dir_all(&dir)?;

    let bin = pixstrip_bin();
    let presets = get_preset_names();

    let mut desktop = format!(
        "[Desktop Entry]\n\
         Type=Service\n\
         X-KDE-ServiceTypes=KonqPopupMenu/Plugin\n\
         MimeType=image/jpeg;image/png;image/webp;image/gif;image/tiff;image/avif;image/bmp;inode/directory;\n\
         Actions=Open;{preset_actions}\n\
         X-KDE-Submenu=Process with Pixstrip\n\
         Icon=applications-graphics-symbolic\n\n\
         [Desktop Action Open]\n\
         Name=Open in Pixstrip...\n\
         Icon=applications-graphics-symbolic\n\
         Exec={bin} --files %F\n\n",
        bin = bin,
        preset_actions = presets
            .iter()
            .enumerate()
            .map(|(i, _)| format!("Preset{}", i))
            .collect::<Vec<_>>()
            .join(";"),
    );

    for (i, name) in presets.iter().enumerate() {
        desktop.push_str(&format!(
            "[Desktop Action Preset{i}]\n\
             Name={name}\n\
             Icon=applications-graphics-symbolic\n\
             Exec={bin} --preset \"{safe_label}\" --files %F\n\n",
            i = i,
            name = name,
            safe_label = shell_safe(name),
            bin = bin,
        ));
    }

    atomic_write(&dolphin_service_path(), &desktop)?;
    Ok(())
}

fn uninstall_dolphin() -> Result<()> {
    let path = dolphin_service_path();
    if path.exists() {
        std::fs::remove_file(path)?;
    }
    Ok(())
}
