// SPDX-License-Identifier: GPL-3.0-or-later
//! Sistema de skins: permite reemplazar iconos, CSS y fondo de ventana
//! mediante carpetas en `~/.gradio/data/skins/<nombre>/`, sin tocar el
//! binario. El skin activo se guarda en `~/.gradio/data/tmp/skin`.

use std::fs;
use std::path::PathBuf;

fn home() -> Option<PathBuf> {
    dirs::home_dir()
}

fn archivo_skin_activo() -> Option<PathBuf> {
    home().map(|h| h.join(".gradio/data/tmp/skin"))
}

fn dir_skins() -> Option<PathBuf> {
    home().map(|h| h.join(".gradio/data/skins"))
}

/// Nombre del skin activo, o `None` si se usa el skin por defecto (embebido).
pub fn nombre_activo() -> Option<String> {
    let path = archivo_skin_activo()?;
    let texto = fs::read_to_string(path).ok()?;
    let nombre = texto.trim();
    if nombre.is_empty() { None } else { Some(nombre.to_string()) }
}

/// Carpeta del skin activo, si existe en disco.
pub fn dir_activo() -> Option<PathBuf> {
    let nombre = nombre_activo()?;
    let dir = dir_skins()?.join(nombre);
    if dir.is_dir() { Some(dir) } else { None }
}

/// Devuelve los bytes del icono `archivo` (ej. "play-on.png") tomados del
/// skin activo si existe, o `fallback` (el recurso embebido) en caso
/// contrario.
pub fn icono(archivo: &str, fallback: &'static [u8]) -> Vec<u8> {
    if let Some(dir) = dir_activo() {
        let ruta = dir.join("iconos").join(archivo);
        if let Ok(bytes) = fs::read(&ruta) {
            return bytes;
        }
    }
    fallback.to_vec()
}

/// Lista los nombres de los skins instalados en `~/.gradio/data/skins/`.
pub fn listar() -> Vec<String> {
    let Some(dir) = dir_skins() else { return Vec::new(); };
    let Ok(entradas) = fs::read_dir(&dir) else { return Vec::new(); };
    let mut nombres: Vec<String> = entradas
        .flatten()
        .filter(|e| e.path().is_dir())
        .filter_map(|e| e.file_name().into_string().ok())
        .collect();
    nombres.sort();
    nombres
}

/// CSS adicional aportado por el skin activo: su `estilo.css` (si existe) más
/// una regla de fondo generada a partir de `fondo.png`/`fondo.jpg` (si
/// existe). `None` si el skin activo no aporta nada.
pub fn css_extra() -> Option<String> {
    let dir = dir_activo()?;
    let mut css = String::new();

    if let Ok(texto) = fs::read_to_string(dir.join("estilo.css")) {
        css.push_str(&texto);
        css.push('\n');
    }

    for nombre_fondo in ["fondo.png", "fondo.jpg", "fondo.jpeg"] {
        let ruta = dir.join(nombre_fondo);
        if ruta.is_file() {
            if let Some(ruta_str) = ruta.to_str() {
                css.push_str(&format!(
                    "window {{ background-image: url(\"file://{}\"); background-size: cover; background-position: center; background-repeat: no-repeat; }}\n",
                    ruta_str
                ));
            }
            break;
        }
    }

    if css.trim().is_empty() { None } else { Some(css) }
}

/// Aplica el CSS adicional del skin activo (si lo hay) al `Display` dado,
/// con prioridad superior a cualquier CSS base ya existente en la app.
pub fn aplicar_css_extra(display: &gdk4::Display) {
    let Some(css) = css_extra() else { return; };
    let provider = gtk4::CssProvider::new();
    provider.load_from_data(&css);
    gtk4::style_context_add_provider_for_display(
        display,
        &provider,
        gtk4::STYLE_PROVIDER_PRIORITY_USER + 100,
    );
}

/// Crea `~/.gradio/data/skins/` si no existe.
pub fn crear_dir_skins_si_falta() {
    if let Some(dir) = dir_skins() {
        let _ = fs::create_dir_all(dir);
    }
}
