use serde::{Deserialize, Serialize};

/// Identifiant unique d'un asset (chemin relatif canonique).
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AssetId(pub String);

impl AssetId {
    pub fn new(path: impl Into<String>) -> Self {
        Self(path.into())
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Display for AssetId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Type d'un asset reconnu par l'éditeur.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AssetKind {
    Mesh,
    Texture,
    Material,
    Audio,
    Scene,
    Font,
    Splat,
    Other(String),
}

impl AssetKind {
    /// Déduit le type depuis l'extension du fichier.
    pub fn from_extension(ext: &str) -> Self {
        match ext.to_ascii_lowercase().as_str() {
            "gltf" | "glb" | "obj" => AssetKind::Mesh,
            "png" | "jpg" | "jpeg" | "webp" | "ktx2" => AssetKind::Texture,
            "wav" | "ogg" | "mp3" | "flac" => AssetKind::Audio,
            "scn" | "ron" => AssetKind::Scene,
            "ttf" | "otf" => AssetKind::Font,
            "splat" | "ply" => AssetKind::Splat,
            other => AssetKind::Other(other.to_string()),
        }
    }
}

/// Métadonnées d'un asset (sans contenu chargé).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AssetMeta {
    pub id: AssetId,
    /// Nom d'affichage (dernière partie du chemin).
    pub name: String,
    pub kind: AssetKind,
    /// Chemin relatif depuis la racine du projet.
    pub path: String,
    pub size_bytes: Option<u64>,
}

impl AssetMeta {
    pub fn new(path: impl Into<String>, size_bytes: Option<u64>) -> Self {
        let path = path.into();
        let name = path
            .rsplit(['/', '\\'])
            .next()
            .unwrap_or(&path)
            .to_string();
        let ext = path.rsplit('.').next().unwrap_or("");
        let kind = AssetKind::from_extension(ext);
        let id = AssetId::new(path.clone());
        Self { id, name, kind, path, size_bytes }
    }
}

/// Accès au catalogue d'assets du projet.
///
/// Implémenté par `BevyEngine` en Jalon 2.
pub trait AssetStore {
    /// Liste tous les assets connus.
    fn list_assets(&self) -> Vec<AssetMeta>;

    /// Cherche un asset par son identifiant.
    fn asset_by_id(&self, id: &AssetId) -> Option<AssetMeta>;

    /// Cherche un asset par son chemin.
    fn asset_by_path(&self, path: &str) -> Option<AssetMeta>;
}

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

    #[test]
    fn asset_meta_from_path() {
        let meta = AssetMeta::new("textures/hero.png", Some(4096));
        assert_eq!(meta.name, "hero.png");
        assert_eq!(meta.kind, AssetKind::Texture);
        assert_eq!(meta.size_bytes, Some(4096));
    }

    #[test]
    fn asset_kind_from_ext() {
        assert_eq!(AssetKind::from_extension("glb"), AssetKind::Mesh);
        assert_eq!(AssetKind::from_extension("wav"), AssetKind::Audio);
        assert_eq!(AssetKind::from_extension("ron"), AssetKind::Scene);
    }
}
