use crate::{builder_fn, AutoDefault};

/// Nombre normalizado para el atributo `name` o similar de HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso:
///
/// - Se eliminan los espacios al principio y al final.
/// - Se convierte a minúsculas.
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// # use pagetop::prelude::*;
/// let name = AttrName::new("  DISplay name ");
/// assert_eq!(name.as_str(), Some("display_name"));
///
/// let empty = AttrName::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct AttrName(Option<String>);

impl AttrName {
    /// Crea un nuevo `AttrName` normalizando el valor.
    pub fn new(value: impl AsRef<str>) -> Self {
        AttrName::default().with_value(value)
    }

    // **< AttrName BUILDER >***********************************************************************

    /// Establece un nombre nuevo normalizando el valor.
    #[builder_fn]
    pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
        let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
        self.0 = if value.is_empty() { None } else { Some(value) };
        self
    }

    // **< AttrName GETTERS >***********************************************************************

    /// Devuelve el nombre normalizado, si existe.
    pub fn get(&self) -> Option<String> {
        self.0.as_ref().cloned()
    }

    /// Devuelve el nombre normalizado (sin clonar), si existe.
    pub fn as_str(&self) -> Option<&str> {
        self.0.as_deref()
    }

    /// Devuelve el nombre normalizado (propiedad), si existe.
    pub fn into_inner(self) -> Option<String> {
        self.0
    }

    /// `true` si no hay valor.
    pub fn is_empty(&self) -> bool {
        self.0.is_none()
    }
}
