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

fn source_icon_dir() -> Option<PathBuf> {
    let exe_path = std::env::current_exe().unwrap_or_default();
    let exe_dir = exe_path.parent().unwrap_or(std::path::Path::new("."));

    let candidates = [
        exe_dir.join("../../outlay-gtk/data/icons/hicolor/scalable/actions"),
        exe_dir.join("../share/icons/hicolor/scalable/actions"),
        PathBuf::from("/usr/share/icons/hicolor/scalable/actions"),
    ];

    for candidate in &candidates {
        if candidate.exists() {
            return Some(candidate.clone());
        }
    }
    None
}

fn cache_icon_dir() -> PathBuf {
    glib::user_cache_dir().join("outlay").join("themed-icons")
}

fn generate_themed_icons(is_dark: bool) {
    let Some(src_dir) = source_icon_dir() else {
        return;
    };

    let cache_dir = cache_icon_dir();
    let actions_dir = cache_dir
        .join("hicolor")
        .join("scalable")
        .join("actions");
    std::fs::create_dir_all(&actions_dir).ok();

    let stroke_color = if is_dark { "#f7f7f7" } else { "#222222" };

    let entries = match std::fs::read_dir(&src_dir) {
        Ok(e) => e,
        Err(_) => return,
    };

    for entry in entries.flatten() {
        let path = entry.path();
        let name = match path.file_name().and_then(|n| n.to_str()) {
            Some(n) => n.to_string(),
            None => continue,
        };

        // Only process non-symbolic SVGs (the ones our code references)
        if name.contains("-symbolic") || !name.ends_with(".svg") {
            continue;
        }

        // Resolve symlinks to read the actual content
        let real_path = match std::fs::canonicalize(&path) {
            Ok(p) => p,
            Err(_) => continue,
        };

        let content = match std::fs::read_to_string(&real_path) {
            Ok(c) => c,
            Err(_) => continue,
        };

        // Replace any hardcoded stroke color with the themed one
        let themed = content
            .replace("stroke=\"#222222\"", &format!("stroke=\"{}\"", stroke_color))
            .replace("stroke=\"#f7f7f7\"", &format!("stroke=\"{}\"", stroke_color));

        let dest = actions_dir.join(&name);
        std::fs::write(&dest, themed).ok();
    }
}

/// Generate a tinted variant of a tabler icon with the given hex color.
/// Returns the icon name to use with `Image::from_icon_name()`.
pub fn get_tinted_icon_name(base_icon: &str, color: &str) -> String {
    // Strip leading # from color
    let color_hex = color.trim_start_matches('#');
    let tinted_name = format!("{}-{}", base_icon, color_hex);

    let cache_dir = cache_icon_dir();
    let actions_dir = cache_dir
        .join("hicolor")
        .join("scalable")
        .join("actions");
    let dest = actions_dir.join(format!("{}.svg", tinted_name));

    // Return cached if it exists
    if dest.exists() {
        return tinted_name;
    }

    // Try to find the source SVG
    let Some(src_dir) = source_icon_dir() else {
        return base_icon.to_string();
    };

    let src_path = src_dir.join(format!("{}.svg", base_icon));
    let real_path = if src_path.exists() {
        std::fs::canonicalize(&src_path).unwrap_or(src_path)
    } else {
        // Try from cache (already themed)
        let cached = actions_dir.join(format!("{}.svg", base_icon));
        if cached.exists() {
            cached
        } else {
            return base_icon.to_string();
        }
    };

    let content = match std::fs::read_to_string(&real_path) {
        Ok(c) => c,
        Err(_) => return base_icon.to_string(),
    };

    // Replace stroke color with the category color
    let color_with_hash = format!("#{}", color_hex);
    let tinted = content
        .replace("stroke=\"#222222\"", &format!("stroke=\"{}\"", color_with_hash))
        .replace("stroke=\"#f7f7f7\"", &format!("stroke=\"{}\"", color_with_hash));

    std::fs::create_dir_all(&actions_dir).ok();
    std::fs::write(&dest, tinted).ok();

    // Force icon theme to notice the new file
    refresh_icon_theme();

    tinted_name
}

/// Force GTK icon theme to rescan its search paths.
pub fn refresh_icon_theme() {
    if let Some(display) = gtk::gdk::Display::default() {
        let theme = gtk::IconTheme::for_display(&display);
        let paths = theme.search_path();
        let path_refs: Vec<&Path> = paths.iter().map(|p| p.as_path()).collect();
        theme.set_search_path(&path_refs);
    }
}

/// Resolve a category icon name, applying color tinting if a color is provided.
pub fn resolve_category_icon(icon: &Option<String>, color: &Option<String>) -> Option<String> {
    match (icon, color) {
        (Some(icon_name), Some(col)) if !col.is_empty() => {
            Some(get_tinted_icon_name(icon_name, col))
        }
        (Some(icon_name), _) => Some(icon_name.clone()),
        _ => None,
    }
}

/// List all available tabler icon names (without -symbolic variants).
pub fn list_tabler_icons() -> Vec<String> {
    let Some(src_dir) = source_icon_dir() else {
        return Vec::new();
    };

    let entries = match std::fs::read_dir(&src_dir) {
        Ok(e) => e,
        Err(_) => return Vec::new(),
    };

    let mut icons: Vec<String> = entries
        .flatten()
        .filter_map(|entry| {
            let name = entry.file_name().to_str()?.to_string();
            if name.starts_with("tabler-") && name.ends_with(".svg") && !name.contains("-symbolic") {
                Some(name.trim_end_matches(".svg").to_string())
            } else {
                None
            }
        })
        .collect();

    icons.sort();
    icons
}

pub fn setup_themed_icons() {
    let display = gtk::gdk::Display::default().expect("Could not get default display");
    let icon_theme = gtk::IconTheme::for_display(&display);

    let style_manager = adw::StyleManager::default();
    let is_dark = style_manager.is_dark();

    generate_themed_icons(is_dark);

    // Register the themed cache directory FIRST (takes precedence)
    let cache_dir = cache_icon_dir();
    icon_theme.add_search_path(&cache_dir);

    // Also register the source directory as fallback (for symbolic variants, etc.)
    if let Some(src_dir) = source_icon_dir() {
        if let Some(parent) = src_dir.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
            icon_theme.add_search_path(parent);
        }
    }

    // Listen for theme changes and regenerate
    style_manager.connect_dark_notify(move |sm| {
        generate_themed_icons(sm.is_dark());

        // Force icon theme to notice the change
        let display = gtk::gdk::Display::default().unwrap();
        let theme = gtk::IconTheme::for_display(&display);
        let paths = theme.search_path();
        let path_refs: Vec<&Path> = paths.iter().map(|p| p.as_path()).collect();
        theme.set_search_path(&path_refs);
    });
}
