//! ID generation utilities (enabled with "ids" feature)

#[cfg(feature = "ids")]
use uuid::Uuid;

/// UUID utilities
#[cfg(feature = "ids")]
pub mod uuid {
    use super::*;

    /// Generate new UUID v4
    pub fn generate() -> String {
        Uuid::new_v4().to_string()
    }

    /// Generate UUID v4 in simple format (no hyphens)
    pub fn generate_simple() -> String {
        Uuid::new_v4().simple().to_string()
    }

    /// Generate short ID (first 8 characters of UUID)
    pub fn generate_short() -> String {
        Uuid::new_v4().simple().to_string()[..8].to_string()
    }

    /// Generate very short ID (first 4 characters of UUID)
    pub fn generate_tiny() -> String {
        Uuid::new_v4().simple().to_string()[..4].to_string()
    }

    /// Validate UUID string
    pub fn is_valid(id: &str) -> bool {
        Uuid::parse_str(id).is_ok()
    }

    /// Parse UUID from string
    pub fn parse(id: &str) -> Result<Uuid, uuid::Error> {
        Uuid::parse_str(id)
    }

    /// Convert UUID to different formats
    pub fn to_simple(id: &str) -> Result<String, uuid::Error> {
        let uuid = Uuid::parse_str(id)?;
        Ok(uuid.simple().to_string())
    }

    /// Convert simple UUID back to hyphenated format
    pub fn to_hyphenated(simple_id: &str) -> Result<String, uuid::Error> {
        if simple_id.len() != 32 {
            return Err(uuid::Error::InvalidLength(simple_id.len()));
        }

        let formatted = format!(
            "{}-{}-{}-{}-{}",
            &simple_id[0..8],
            &simple_id[8..12],
            &simple_id[12..16],
            &simple_id[16..20],
            &simple_id[20..32]
        );

        Uuid::parse_str(&formatted)?;
        Ok(formatted)
    }
}

/// Slug utilities for human-readable IDs
pub mod slug {
    use crate::utils::string;

    /// Generate slug from title
    pub fn from_title(title: &str) -> String {
        string::to_kebab_case(title)
            .chars()
            .filter(|c| c.is_alphanumeric() || *c == '-')
            .collect::<String>()
            .trim_matches('-')
            .to_string()
    }

    /// Generate unique slug by adding suffix if needed
    pub fn make_unique<F>(base_slug: &str, exists_fn: F) -> String
    where
        F: Fn(&str) -> bool,
    {
        if !exists_fn(base_slug) {
            return base_slug.to_string();
        }

        for i in 1..1000 {
            let candidate = format!("{}-{}", base_slug, i);
            if !exists_fn(&candidate) {
                return candidate;
            }
        }

        // Fallback: add UUID suffix
        #[cfg(feature = "ids")]
        {
            format!("{}-{}", base_slug, uuid::generate_short())
        }
        #[cfg(not(feature = "ids"))]
        {
            use std::time::{SystemTime, UNIX_EPOCH};
            let timestamp = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_millis();
            format!("{}-{}", base_slug, timestamp)
        }
    }

    /// Validate slug format
    pub fn is_valid(slug: &str) -> bool {
        !slug.is_empty()
            && slug.len() <= 100
            && slug.chars().all(|c| c.is_alphanumeric() || c == '-')
            && !slug.starts_with('-')
            && !slug.ends_with('-')
            && !slug.contains("--")
    }

    /// Clean up slug to make it valid
    pub fn clean(input: &str) -> String {
        let mut cleaned = from_title(input);

        // Remove multiple consecutive hyphens
        while cleaned.contains("--") {
            cleaned = cleaned.replace("--", "-");
        }

        // Trim hyphens from start and end
        cleaned = cleaned.trim_matches('-').to_string();

        // Truncate if too long
        if cleaned.len() > 100 {
            cleaned = cleaned[..100].trim_end_matches('-').to_string();
        }

        // Ensure not empty
        if cleaned.is_empty() {
            cleaned = "untitled".to_string();
        }

        cleaned
    }
}

/// Content ID utilities
pub mod content {
    use super::*;

    /// Generate content ID from type and title
    pub fn generate_id(content_type: &str, title: &str) -> String {
        let slug = slug::clean(title);
        format!("{}:{}", content_type, slug)
    }

    /// Generate content ID with timestamp
    pub fn generate_id_with_timestamp(content_type: &str, title: &str) -> String {
        let slug = slug::clean(title);

        #[cfg(feature = "time")]
        {
            use crate::utils::time;
            let timestamp = time::now_iso();
            let date_part = timestamp.split('T').next().unwrap_or("unknown");
            format!("{}:{}:{}", content_type, date_part, slug)
        }

        #[cfg(not(feature = "time"))]
        {
            use std::time::{SystemTime, UNIX_EPOCH};
            let timestamp = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs();
            format!("{}:{}:{}", content_type, timestamp, slug)
        }
    }

    /// Parse content ID into parts
    pub fn parse_id(id: &str) -> Option<(String, String)> {
        let parts: Vec<&str> = id.splitn(2, ':').collect();
        if parts.len() == 2 {
            Some((parts[0].to_string(), parts[1].to_string()))
        } else {
            None
        }
    }

    /// Validate content ID format
    pub fn is_valid_id(id: &str) -> bool {
        if let Some((content_type, slug_part)) = parse_id(id) {
            !content_type.is_empty()
                && !slug_part.is_empty()
                && content_type.chars().all(|c| c.is_alphanumeric() || c == '_')
                && slug::is_valid(&slug_part.split(':').last().unwrap_or(""))
        } else {
            false
        }
    }
}

// No-op implementations when ids feature is not enabled
#[cfg(not(feature = "ids"))]
pub mod uuid {
    pub fn generate() -> String {
        use std::time::{SystemTime, UNIX_EPOCH};
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis();
        format!("id-{}", timestamp)
    }

    pub fn generate_simple() -> String {
        generate()
    }

    pub fn generate_short() -> String {
        generate()[..8].to_string()
    }

    pub fn generate_tiny() -> String {
        generate()[..4].to_string()
    }

    pub fn is_valid(_id: &str) -> bool {
        false
    }
}

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

    #[test]
    fn test_slug_generation() {
        assert_eq!(slug::from_title("Hello World"), "hello-world");
        assert_eq!(slug::from_title("My Blog Post!"), "my-blog-post");
        assert_eq!(slug::from_title("CamelCase Title"), "camel-case-title");
        assert_eq!(slug::from_title("Test   Spaces"), "test-spaces");
    }

    #[test]
    fn test_slug_validation() {
        assert!(slug::is_valid("hello-world"));
        assert!(slug::is_valid("my-post-123"));
        assert!(!slug::is_valid(""));
        assert!(!slug::is_valid("-invalid"));
        assert!(!slug::is_valid("invalid-"));
        assert!(!slug::is_valid("double--hyphen"));
        assert!(!slug::is_valid("with spaces"));
        assert!(!slug::is_valid("with/slash"));
    }

    #[test]
    fn test_slug_cleaning() {
        assert_eq!(slug::clean("--bad--start--"), "bad-start");
        assert_eq!(slug::clean(""), "untitled");
        assert_eq!(slug::clean("Good Title"), "good-title");

        // Test truncation
        let long_title = "a".repeat(150);
        let cleaned = slug::clean(&long_title);
        assert!(cleaned.len() <= 100);
        assert!(!cleaned.ends_with('-'));
    }

    #[test]
    fn test_content_id_generation() {
        let id = content::generate_id("blog", "My First Post");
        assert_eq!(id, "blog:my-first-post");

        let id_with_timestamp = content::generate_id_with_timestamp("blog", "My Post");
        assert!(id_with_timestamp.starts_with("blog:"));
        assert!(id_with_timestamp.ends_with(":my-post"));
    }

    #[test]
    fn test_content_id_parsing() {
        let (content_type, slug) = content::parse_id("blog:my-post").unwrap();
        assert_eq!(content_type, "blog");
        assert_eq!(slug, "my-post");

        let (content_type, slug) = content::parse_id("blog:2023-01-01:my-post").unwrap();
        assert_eq!(content_type, "blog");
        assert_eq!(slug, "2023-01-01:my-post");

        assert!(content::parse_id("invalid-id").is_none());
    }

    #[test]
    fn test_content_id_validation() {
        assert!(content::is_valid_id("blog:my-post"));
        assert!(content::is_valid_id("news_article:breaking-news"));
        assert!(!content::is_valid_id(""));
        assert!(!content::is_valid_id("blog"));
        assert!(!content::is_valid_id(":my-post"));
        assert!(!content::is_valid_id("blog:"));
    }

    #[cfg(feature = "ids")]
    #[test]
    fn test_uuid_generation() {
        let id = uuid::generate();
        assert!(uuid::is_valid(&id));
        assert_eq!(id.len(), 36); // Standard UUID format with hyphens

        let simple_id = uuid::generate_simple();
        assert_eq!(simple_id.len(), 32); // No hyphens

        let short_id = uuid::generate_short();
        assert_eq!(short_id.len(), 8);

        let tiny_id = uuid::generate_tiny();
        assert_eq!(tiny_id.len(), 4);
    }

    #[cfg(feature = "ids")]
    #[test]
    fn test_uuid_conversion() {
        let id = uuid::generate();
        let simple = uuid::to_simple(&id).unwrap();
        let back_to_hyphenated = uuid::to_hyphenated(&simple).unwrap();

        assert_eq!(id.to_lowercase(), back_to_hyphenated.to_lowercase());
    }
}
