use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use anyhow::{Context, Result};
#[cfg(feature = "content-static")]
use lru::LruCache;
use rustelo_core_lib::cache::ContentCacheKey;
use rustelo_core_lib::fluent::{ContentQuery, ContentState, ContentType, PageContent};
use tokio::time::{Duration, Instant};
use tracing::{debug, info, warn};
use uuid::Uuid;

#[cfg(feature = "content-static")]
use super::file_loader::FileContentLoader;
#[cfg(feature = "content-static")]
use super::html_processor::HtmlProcessor;
#[cfg(feature = "content-static")]
use super::indexer::{ContentIndexer, PostIndexEntry};
#[cfg(feature = "content-static")]
use super::processor::MarkdownProcessor;
#[cfg(feature = "content-db")]
use super::repository::ContentRepository;
#[cfg(feature = "content-static")]
use super::version_control::VersionControlManager;

pub enum ContentSource {
    Database,
    #[allow(dead_code)]
    Files,
    #[allow(dead_code)]
    Both,
}

pub struct ContentService {
    #[cfg(feature = "content-db")]
    repository: Arc<ContentRepository>,
    #[cfg(feature = "content-static")]
    file_loader: Option<FileContentLoader>,
    #[cfg(feature = "content-static")]
    html_processor: HtmlProcessor,
    #[cfg(feature = "content-static")]
    processor: MarkdownProcessor,
    #[cfg(feature = "content-static")]
    indexer: Option<ContentIndexer>,
    #[cfg(feature = "content-static")]
    version_control: Option<VersionControlManager>,
    #[cfg(feature = "content-static")]
    content_root: PathBuf,
    content_source: ContentSource,
    cache: tokio::sync::RwLock<HashMap<String, PageContent>>,
    translations_cache: tokio::sync::RwLock<HashMap<String, HashMap<String, String>>>,
    #[cfg(feature = "content-static")]
    lru_cache: Arc<tokio::sync::RwLock<LruCache<String, (PageContent, Instant)>>>,
    cache_ttl: Duration,
    enable_cache: bool,
}

impl ContentService {
    #[cfg(feature = "content-db")]
    pub fn new(repository: Arc<ContentRepository>) -> Self {
        Self {
            repository,
            #[cfg(feature = "content-static")]
            file_loader: None,
            #[cfg(feature = "content-static")]
            html_processor: HtmlProcessor::new(),
            #[cfg(feature = "content-static")]
            processor: MarkdownProcessor::new(),
            #[cfg(feature = "content-static")]
            indexer: None,
            #[cfg(feature = "content-static")]
            version_control: None,
            #[cfg(feature = "content-static")]
            content_root: std::env::var("SITE_CONTENT_PATH")
                .unwrap_or_else(|_| "site/content".to_string())
                .into(),
            content_source: ContentSource::Database,
            cache: tokio::sync::RwLock::new(HashMap::new()),
            translations_cache: tokio::sync::RwLock::new(HashMap::new()),
            #[cfg(feature = "content-static")]
            lru_cache: Arc::new(tokio::sync::RwLock::new(LruCache::new(
                1000.try_into().unwrap(),
            ))),
            cache_ttl: Duration::from_secs(3600), // 1 hour default
            enable_cache: true,
        }
    }

    #[cfg(feature = "content-static")]
    pub fn new_static() -> Self {
        Self {
            #[cfg(feature = "content-db")]
            repository: Arc::new(crate::content::repository::ContentRepository::new(
                crate::database::DatabaseConnection::new_mock(),
            )),
            #[cfg(feature = "content-static")]
            file_loader: None,
            #[cfg(feature = "content-static")]
            html_processor: HtmlProcessor::new(),
            #[cfg(feature = "content-static")]
            processor: MarkdownProcessor::new(),
            #[cfg(feature = "content-static")]
            indexer: None,
            #[cfg(feature = "content-static")]
            version_control: None,
            #[cfg(feature = "content-static")]
            content_root: std::env::var("SITE_CONTENT_PATH")
                .unwrap_or_else(|_| "site/content".to_string())
                .into(),
            content_source: ContentSource::Files,
            cache: tokio::sync::RwLock::new(HashMap::new()),
            translations_cache: tokio::sync::RwLock::new(HashMap::new()),
            #[cfg(feature = "content-static")]
            lru_cache: Arc::new(tokio::sync::RwLock::new(LruCache::new(
                1000.try_into().unwrap(),
            ))),
            cache_ttl: Duration::from_secs(3600), // 1 hour default
            enable_cache: true,
        }
    }

    #[cfg(feature = "content-static")]
    #[allow(dead_code)]
    pub fn with_file_loader<P: AsRef<Path>>(mut self, content_dir: P) -> Self {
        self.file_loader = Some(FileContentLoader::new(content_dir));
        self.content_source = ContentSource::Both;
        self
    }

    /// Configure with content indexer for fast filtering
    #[cfg(feature = "content-static")]
    pub fn with_indexer<P: AsRef<Path>>(mut self, output_dir: P) -> Self {
        self.indexer = Some(ContentIndexer::new(
            self.content_root.clone(),
            output_dir.as_ref().to_path_buf(),
        ));
        self
    }

    /// Configure with custom indexer settings
    #[cfg(feature = "content-static")]
    pub fn with_custom_indexer(mut self, indexer: ContentIndexer) -> Self {
        self.indexer = Some(indexer);
        self
    }

    /// Enable version control for content
    #[cfg(feature = "content-static")]
    pub fn with_version_control(mut self) -> Self {
        let backend =
            super::version_control::VersionControlFactory::create_file_backend(&self.content_root);
        self.version_control = Some(VersionControlManager::new(backend, None));
        self
    }

    pub fn with_source(mut self, source: ContentSource) -> Self {
        self.content_source = source;
        self
    }

    pub fn with_cache(mut self, enable: bool) -> Self {
        self.enable_cache = enable;
        self
    }

    pub async fn get_content_by_slug(&self, slug: &str) -> Result<Option<PageContent>> {
        // Check cache first
        if self.enable_cache {
            let cache = self.cache.read().await;
            if let Some(content) = cache.get(slug) {
                return Ok(Some(content.clone()));
            }
        }

        let content = match &self.content_source {
            ContentSource::Database => {
                #[cfg(feature = "content-db")]
                {
                    self.repository.get_content_by_slug(slug).await?
                }
                #[cfg(not(feature = "content-db"))]
                {
                    None
                }
            }
            ContentSource::Files => {
                #[cfg(feature = "content-static")]
                {
                    if let Some(loader) = &self.file_loader {
                        loader.load_by_slug(slug)?
                    } else {
                        None
                    }
                }
                #[cfg(not(feature = "content-static"))]
                {
                    None
                }
            }
            ContentSource::Both => {
                #[cfg(all(feature = "content-db", feature = "content-static"))]
                {
                    // Try database first, then files
                    if let Some(content) = self.repository.get_content_by_slug(slug).await? {
                        Some(content)
                    } else if let Some(loader) = &self.file_loader {
                        loader.load_by_slug(slug)?
                    } else {
                        None
                    }
                }
                #[cfg(not(all(feature = "content-db", feature = "content-static")))]
                {
                    None
                }
            }
        };

        // Cache the result
        if self.enable_cache {
            if let Some(ref content) = content {
                let mut cache = self.cache.write().await;
                cache.insert(slug.to_string(), content.clone());
            }
        }

        Ok(content)
    }

    pub async fn get_content_by_id(&self, _id: Uuid) -> Result<Option<PageContent>> {
        // For file-based content, we don't have IDs, so only check database
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                #[cfg(feature = "content-db")]
                {
                    self.repository.get_content_by_id(&_id).await
                }
                #[cfg(not(feature = "content-db"))]
                {
                    Ok(None)
                }
            }
            ContentSource::Files => Ok(None),
        }
    }

    pub async fn create_content(&self, _content: &PageContent) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                #[cfg(feature = "content-db")]
                {
                    self.repository.create_content(_content).await
                }
                #[cfg(not(feature = "content-db"))]
                {
                    Err(anyhow::anyhow!("Database feature not enabled"))
                }
            }
            ContentSource::Files => Err(anyhow::anyhow!("Cannot create content in file-only mode")),
        }
    }

    pub async fn update_content(&self, _content: &PageContent) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                #[cfg(feature = "content-db")]
                {
                    self.repository.update_content(_content).await
                }
                #[cfg(not(feature = "content-db"))]
                {
                    Err(anyhow::anyhow!("Database feature not enabled"))
                }
            }
            ContentSource::Files => Err(anyhow::anyhow!("Cannot update content in file-only mode")),
        }
    }

    pub async fn delete_content(&self, _id: Uuid) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                #[cfg(feature = "content-db")]
                {
                    self.repository.delete_content(&_id).await
                }
                #[cfg(not(feature = "content-db"))]
                {
                    Err(anyhow::anyhow!("Database feature not enabled"))
                }
            }
            ContentSource::Files => Err(anyhow::anyhow!("Cannot delete content in file-only mode")),
        }
    }

    pub async fn query_contents(&self, query: &ContentQuery) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement query_contents method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let mut contents = loader.load_all_content()?;

                    // Apply filters
                    if let Some(content_type) = &query.content_type {
                        contents.retain(|c| &c.content_type == content_type);
                    }

                    if let Some(state) = &query.state {
                        contents.retain(|c| &c.state == state);
                    }

                    if let Some(author_id) = &query.author_id {
                        contents.retain(|c| c.author_id == Some(*author_id));
                    }

                    if let Some(category) = &query.category {
                        contents.retain(|c| c.category.as_ref() == Some(category));
                    }

                    if let Some(tags) = &query.tags {
                        contents.retain(|c| tags.iter().any(|tag| c.tags.contains(tag)));
                    }

                    if let Some(require_login) = &query.require_login {
                        contents.retain(|c| c.require_login == *require_login);
                    }

                    if let Some(search) = &query.search {
                        let search_lower = search.to_lowercase();
                        contents.retain(|c| {
                            c.title.to_lowercase().contains(&search_lower)
                                || c.content.to_lowercase().contains(&search_lower)
                                || c.excerpt
                                    .as_ref()
                                    .map_or(false, |e| e.to_lowercase().contains(&search_lower))
                        });
                    }

                    // Apply sorting
                    contents.sort_by(|a, b| {
                        match query.sort_by.as_deref() {
                            Some("title") => a.title.cmp(&b.title),
                            Some("created_at") => a.created_at.cmp(&b.created_at),
                            Some("updated_at") => a.updated_at.cmp(&b.updated_at),
                            Some("published_at") => a.published_at.cmp(&b.published_at),
                            Some("view_count") => a.view_count.cmp(&b.view_count),
                            _ => b.created_at.cmp(&a.created_at), // Default: newest first
                        }
                    });

                    if query.sort_order.as_deref() == Some("DESC") {
                        contents.reverse();
                    }

                    // Apply pagination
                    let offset = query.offset.unwrap_or(0) as usize;
                    let limit = query.limit.map(|l| l as usize);

                    if offset > 0 {
                        contents = contents.into_iter().skip(offset).collect();
                    }

                    if let Some(limit) = limit {
                        contents.truncate(limit);
                    }

                    Ok(contents)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // Combine results from both sources
                // TODO: Implement query_contents method
                let mut db_contents = vec![];

                if let Some(loader) = &self.file_loader {
                    let file_contents = loader.load_all_content()?;

                    // Filter file contents based on query
                    let filtered_file_contents: Vec<_> = file_contents
                        .into_iter()
                        .filter(|c| {
                            if let Some(content_type) = &query.content_type {
                                if &c.content_type != content_type {
                                    return false;
                                }
                            }
                            if let Some(state) = &query.state {
                                if &c.state != state {
                                    return false;
                                }
                            }
                            true
                        })
                        .collect();

                    // Merge contents, avoiding duplicates by slug
                    let mut slugs: std::collections::HashSet<String> = db_contents
                        .iter()
                        .map(|c: &PageContent| c.slug.clone())
                        .collect();

                    for content in filtered_file_contents {
                        if !slugs.contains(&content.slug) {
                            slugs.insert(content.slug.clone());
                            db_contents.push(content);
                        }
                    }
                }

                Ok(db_contents)
            }
        }
    }

    pub async fn get_published_contents(&self, limit: Option<i64>) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_state(ContentState::Published)
            .with_pagination(limit.unwrap_or(50), 0);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_type(
        &self,
        content_type: ContentType,
    ) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_content_type(content_type)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_author(&self, author_id: Uuid) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_author(author_id)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_category(&self, category: &str) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_category(category.to_string())
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    #[allow(dead_code)]
    pub async fn get_contents_by_tags(&self, tags: Vec<String>) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_tags(tags)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn search_contents(
        &self,
        search_term: &str,
        limit: Option<i64>,
    ) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_search(search_term.to_string())
            .with_state(ContentState::Published)
            .with_pagination(limit.unwrap_or(20), 0);

        self.query_contents(&query).await
    }

    pub async fn increment_view_count(&self, _id: Uuid) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement increment_view_count method
                // self.repository.increment_view_count(&id).await?;
            }
            ContentSource::Files => {
                // For file-based content, we can't increment view count
                tracing::warn!("Cannot increment view count for file-based content");
            }
        }

        Ok(())
    }

    pub async fn get_content_stats(&self) -> Result<ContentStats> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_content_count method
                let total_count = 0;
                // TODO: Implement get_published_count method
                let published_count = 0;
                // TODO: Implement get_draft_count method
                let draft_count = 0;

                Ok(ContentStats {
                    total_count,
                    published_count,
                    draft_count,
                })
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_all_content()?;
                    let total_count = contents.len() as i64;
                    let published_count =
                        contents.iter().filter(|c| c.is_published()).count() as i64;
                    let draft_count = contents
                        .iter()
                        .filter(|c| matches!(c.state, ContentState::Draft))
                        .count() as i64;

                    Ok(ContentStats {
                        total_count,
                        published_count,
                        draft_count,
                    })
                } else {
                    Ok(ContentStats {
                        total_count: 0,
                        published_count: 0,
                        draft_count: 0,
                    })
                }
            }
        }
    }

    pub async fn get_recent_contents(&self, limit: i64) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_state(ContentState::Published)
            .with_pagination(limit, 0);

        self.query_contents(&query).await
    }

    pub async fn get_popular_contents(&self, limit: i64) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_popular_contents method
                Ok(vec![])
            }
            ContentSource::Files => {
                // For file-based content, we can't determine popularity
                self.get_recent_contents(limit).await
            }
        }
    }

    pub async fn get_all_tags(&self) -> Result<Vec<String>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement get_all_tags method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut tags = std::collections::HashSet::new();
                    for content in contents {
                        for tag in content.tags {
                            tags.insert(tag);
                        }
                    }
                    let mut sorted_tags: Vec<_> = tags.into_iter().collect();
                    sorted_tags.sort();
                    Ok(sorted_tags)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // TODO: Implement get_all_tags method
                let mut db_tags = vec![];

                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut file_tags = std::collections::HashSet::new();
                    for content in contents {
                        for tag in content.tags {
                            file_tags.insert(tag);
                        }
                    }

                    // Merge tags
                    let mut all_tags: std::collections::HashSet<_> = db_tags.into_iter().collect();
                    all_tags.extend(file_tags);

                    let mut sorted_tags: Vec<_> = all_tags.into_iter().collect();
                    sorted_tags.sort();
                    db_tags = sorted_tags;
                }

                Ok(db_tags)
            }
        }
    }

    pub async fn get_all_categories(&self) -> Result<Vec<String>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement get_all_categories method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut categories = std::collections::HashSet::new();
                    for content in contents {
                        if let Some(category) = content.category {
                            categories.insert(category);
                        }
                    }
                    let mut sorted_categories: Vec<_> = categories.into_iter().collect();
                    sorted_categories.sort();
                    Ok(sorted_categories)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // TODO: Implement get_all_categories method
                let mut db_categories = vec![];

                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut file_categories = std::collections::HashSet::new();
                    for content in contents {
                        if let Some(category) = content.category {
                            file_categories.insert(category);
                        }
                    }

                    // Merge categories
                    let mut all_categories: std::collections::HashSet<_> =
                        db_categories.into_iter().collect();
                    all_categories.extend(file_categories);

                    let mut sorted_categories: Vec<_> = all_categories.into_iter().collect();
                    sorted_categories.sort();
                    db_categories = sorted_categories;
                }

                Ok(db_categories)
            }
        }
    }

    pub async fn clear_cache(&self) -> Result<()> {
        if self.enable_cache {
            let mut cache = self.cache.write().await;
            cache.clear();
        }
        Ok(())
    }

    pub async fn reload_file_content(&self) -> Result<()> {
        if let Some(_loader) = &self.file_loader {
            // Clear cache to force reload
            self.clear_cache().await?;
            tracing::info!("File content cache cleared for reload");
        }
        Ok(())
    }

    pub async fn publish_scheduled_content(&self) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_scheduled_contents method
                let scheduled_contents: Vec<PageContent> = vec![];
                for content in &scheduled_contents {
                    // TODO: Implement publish_scheduled_content method
                    // self.repository.publish_scheduled_content(&content.id).await?;

                    // Clear cache for the content
                    if self.enable_cache {
                        let mut cache = self.cache.write().await;
                        cache.remove(&content.slug);
                    }
                }
                Ok(scheduled_contents)
            }
            ContentSource::Files => {
                // File-based content doesn't support scheduled publishing
                Ok(Vec::new())
            }
        }
    }

    /// Get content by category with caching
    pub async fn get_content_by_category(
        &self,
        content_type: &str,
        language: &str,
        category: &str,
    ) -> Result<Vec<PageContent>> {
        let cache_key = format!("{}/{}/{}/index", content_type, language, category);

        // Check cache first
        if self.enable_cache {
            let cache = self.cache.read().await;
            if let Some(content) = cache.get(&cache_key) {
                // For category cache, we store a single PageContent with serialized data
                // This is a simplified approach; in production, consider a proper cache
                // structure
                return Ok(vec![content.clone()]);
            }
        }

        // TODO: Implement actual file loading for category-based content
        // This would read the category index.json and load individual posts
        let contents: Vec<PageContent> = Vec::new();

        // Cache the result
        if self.enable_cache && !contents.is_empty() {
            let mut cache = self.cache.write().await;
            // Store the first item as representative (simplified caching)
            if let Some(first_content) = contents.first() {
                cache.insert(cache_key, first_content.clone());
            }
        }

        Ok(contents)
    }

    /// Invalidate cache for a specific category
    pub async fn invalidate_category_cache(
        &self,
        content_type: &str,
        language: &str,
        category: &str,
    ) {
        if self.enable_cache {
            let cache_key = ContentCacheKey::for_category_index(content_type, language, category);
            let mut cache = self.cache.write().await;
            cache.remove(&cache_key.cache_key());
        }
    }

    /// Invalidate cache for a specific post
    pub async fn invalidate_post_cache(
        &self,
        content_type: &str,
        language: &str,
        category: &str,
        slug: &str,
    ) {
        if self.enable_cache {
            let cache_key = ContentCacheKey::for_post(content_type, language, category, slug);
            let mut cache = self.cache.write().await;
            cache.remove(&cache_key.cache_key());
            // Also remove the old-style cache key for backward compatibility
            cache.remove(slug);
        }
    }

    /// Invalidate all cache entries for a content type and language
    pub async fn invalidate_content_type_cache(&self, content_type: &str, language: &str) {
        if self.enable_cache {
            let prefix = format!("{}/{}/", content_type, language);
            let mut cache = self.cache.write().await;
            let keys_to_remove: Vec<String> = cache
                .keys()
                .filter(|key| key.starts_with(&prefix))
                .cloned()
                .collect();

            for key in keys_to_remove {
                cache.remove(&key);
            }
        }
    }

    /// Invalidate all cache entries for a content type across all languages
    /// Use when global meta.toml changes affect all language variants
    pub async fn invalidate_global_metadata_cache(&self, content_type: &str) {
        if self.enable_cache {
            let prefix = format!("{}/", content_type);
            let mut cache = self.cache.write().await;
            let keys_to_remove: Vec<String> = cache
                .keys()
                .filter(|key| key.starts_with(&prefix))
                .cloned()
                .collect();

            for key in keys_to_remove {
                cache.remove(&key);
            }
        }
    }

    /// Invalidate translations cache for a specific post
    pub async fn invalidate_translations_cache(&self, content_type: &str, post_id: &str) {
        if self.enable_cache {
            let cache_key = ContentCacheKey::for_translations(content_type, post_id);
            let mut cache = self.translations_cache.write().await;
            cache.remove(&cache_key.cache_key());
        }
    }

    /// Cache translations for a post
    pub async fn cache_translations(
        &self,
        content_type: &str,
        post_id: &str,
        translations: HashMap<String, String>,
    ) {
        if self.enable_cache {
            let cache_key = ContentCacheKey::for_translations(content_type, post_id);
            let mut cache = self.translations_cache.write().await;
            cache.insert(cache_key.cache_key(), translations);
        }
    }

    /// Get cached translations for a post
    pub async fn get_cached_translations(
        &self,
        content_type: &str,
        post_id: &str,
    ) -> Option<HashMap<String, String>> {
        if self.enable_cache {
            let cache_key = ContentCacheKey::for_translations(content_type, post_id);
            let cache = self.translations_cache.read().await;
            cache.get(&cache_key.cache_key()).cloned()
        } else {
            None
        }
    }

    /// Get cache statistics for monitoring
    pub async fn get_cache_stats(&self) -> (usize, Vec<String>) {
        if self.enable_cache {
            let cache = self.cache.read().await;
            let size = cache.len();
            let keys: Vec<String> = cache.keys().take(10).cloned().collect(); // Sample keys
            (size, keys)
        } else {
            (0, Vec::new())
        }
    }

    // Runtime Content Processing Methods

    /// Load content from file (markdown or HTML) with runtime processing and
    /// caching
    #[cfg(feature = "content-static")]
    pub async fn load_content_runtime(
        &self,
        content_type: &str,
        language: &str,
        slug: &str,
    ) -> Result<Option<PageContent>> {
        let cache_key = format!("{}:{}:{}", content_type, language, slug);

        // Check LRU cache first
        if self.enable_cache {
            let mut lru_cache = self.lru_cache.write().await;
            if let Some((content, cached_at)) = lru_cache.get(&cache_key) {
                // Check if cache is still valid
                if cached_at.elapsed() < self.cache_ttl {
                    debug!("Cache hit for {}", cache_key);
                    return Ok(Some(content.clone()));
                } else {
                    // Remove expired entry
                    lru_cache.pop(&cache_key);
                    debug!("Cache expired for {}", cache_key);
                }
            }
        }

        // Try different file extensions in order of preference
        let extensions = [".md", ".html", ".markdown"];
        let base_path = self.content_root.join(content_type).join(language);

        let (file_path, content_format) = {
            let mut found_path = None;
            let mut detected_format = rustelo_core_lib::fluent::ContentFormat::Markdown;

            for ext in &extensions {
                let path = base_path.join(format!("{}{}", slug, ext));
                if path.exists() {
                    detected_format = match *ext {
                        ".html" => rustelo_core_lib::fluent::ContentFormat::Html,
                        ".md" | ".markdown" => rustelo_core_lib::fluent::ContentFormat::Markdown,
                        _ => rustelo_core_lib::fluent::ContentFormat::Markdown,
                    };
                    found_path = Some(path);
                    break;
                }
            }

            match found_path {
                Some(path) => (path, detected_format),
                None => {
                    debug!(
                        "Content file not found for slug '{}' in {:?}",
                        slug, base_path
                    );
                    return Ok(None);
                }
            }
        };

        // Read file content
        let raw_content = tokio::fs::read_to_string(&file_path)
            .await
            .with_context(|| format!("Failed to read content file: {:?}", file_path))?;

        // Process content based on format
        let page_content = match content_format {
            rustelo_core_lib::fluent::ContentFormat::Markdown => {
                let processed = self
                    .processor
                    .process(&raw_content)
                    .with_context(|| format!("Failed to process markdown: {:?}", file_path))?;
                self.processor
                    .to_page_content(processed, content_type, language)
                    .with_context(|| format!("Failed to convert to PageContent: {:?}", file_path))?
            }
            rustelo_core_lib::fluent::ContentFormat::Html => {
                let processed = self
                    .html_processor
                    .process(&raw_content)
                    .with_context(|| format!("Failed to process HTML: {:?}", file_path))?;
                self.html_to_page_content(processed, content_type, language, slug)
                    .with_context(|| {
                        format!("Failed to convert HTML to PageContent: {:?}", file_path)
                    })?
            }
            _ => {
                // Default to markdown processing for other formats
                let processed = self
                    .processor
                    .process(&raw_content)
                    .with_context(|| format!("Failed to process content: {:?}", file_path))?;
                self.processor
                    .to_page_content(processed, content_type, language)
                    .with_context(|| format!("Failed to convert to PageContent: {:?}", file_path))?
            }
        };

        // Cache the result
        if self.enable_cache {
            let mut lru_cache = self.lru_cache.write().await;
            lru_cache.put(cache_key.clone(), (page_content.clone(), Instant::now()));
            debug!("Cached content for {}", cache_key);
        }

        info!(
            "Loaded and processed content: {}/{}/{}",
            content_type, language, slug
        );
        Ok(Some(page_content))
    }

    /// Convert processed HTML to PageContent
    #[cfg(feature = "content-static")]
    fn html_to_page_content(
        &self,
        processed: super::html_processor::ProcessedHtml,
        content_type: &str,
        _language: &str,
        slug: &str,
    ) -> Result<PageContent> {
        use rustelo_core_lib::fluent::{ContentFormat, ContentState, ContentType, PageContent};
        use uuid::Uuid;

        // Extract title from metadata or first heading
        let title = processed
            .metadata
            .title
            .clone()
            .or_else(|| processed.headings.first().map(|h| h.title.clone()))
            .unwrap_or_else(|| slug.replace('-', " ").to_string());

        // Extract excerpt from metadata or description
        let excerpt = processed.metadata.description.clone().unwrap_or_else(|| {
            // Generate excerpt from content (first 200 characters)
            let text_content = processed
                .sanitized_html
                .chars()
                .filter(|c| c.is_alphanumeric() || c.is_whitespace())
                .collect::<String>();
            let words: Vec<&str> = text_content.split_whitespace().take(30).collect();
            words.join(" ")
        });

        // Parse content type
        let parsed_content_type = match content_type {
            "blog" => ContentType::Blog,
            "page" => ContentType::Page,
            "article" => ContentType::Article,
            "documentation" => ContentType::Documentation,
            "tutorial" => ContentType::Tutorial,
            _ => ContentType::Custom(content_type.to_string()),
        };

        // Parse dates
        let published_at = processed
            .metadata
            .published_time
            .and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
            .map(|dt| dt.with_timezone(&chrono::Utc));

        let updated_at = processed
            .metadata
            .modified_time
            .and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
            .map(|dt| dt.with_timezone(&chrono::Utc));

        // Build tags from metadata
        let mut tags = processed.metadata.keywords.clone();
        tags.extend(processed.metadata.article_tags.clone());

        let now = chrono::Utc::now();

        // Create metadata HashMap
        let mut meta = std::collections::HashMap::new();
        meta.insert("word_count".to_string(), processed.word_count.to_string());
        meta.insert(
            "reading_time_minutes".to_string(),
            processed.reading_time_minutes.to_string(),
        );
        meta.insert(
            "headings_count".to_string(),
            processed.headings.len().to_string(),
        );
        meta.insert("links_count".to_string(), processed.links.len().to_string());
        meta.insert(
            "images_count".to_string(),
            processed.images.len().to_string(),
        );

        // Add canonical URL if present
        if let Some(canonical) = processed.metadata.canonical_url {
            meta.insert("canonical_url".to_string(), canonical);
        }

        // Add language if detected
        if let Some(lang) = processed.metadata.language {
            meta.insert("detected_language".to_string(), lang);
        }

        // Add custom metadata
        for (key, value) in processed.metadata.custom_meta {
            meta.insert(format!("custom_{}", key), value);
        }

        // Create PageContent
        Ok(PageContent {
            id: Uuid::new_v4(),
            slug: slug.to_string(),
            title,
            name: slug.replace('-', "_"), // Use slug as name
            author: processed
                .metadata
                .author
                .or(processed.metadata.article_author),
            author_id: None,
            content_type: parsed_content_type,
            content_format: ContentFormat::Html,
            content: processed.sanitized_html,
            container: content_type.to_string(),
            state: ContentState::Published,
            require_login: false,
            date_init: published_at.unwrap_or(now),
            date_end: None,
            created_at: now,
            updated_at: updated_at.unwrap_or(now),
            published_at,
            metadata: meta,
            tags,
            category: processed.metadata.article_section,
            featured_image: None, // Could be extracted from first image
            excerpt: Some(excerpt),
            seo_title: processed.metadata.title.clone(),
            seo_description: processed.metadata.description.clone(),
            allow_comments: true,
            view_count: 0,
            sort_order: 0,
        })
    }

    /// Load content from any available method (runtime or static)
    #[cfg(feature = "content-static")]
    pub async fn get_content_dynamic(
        &self,
        content_type: &str,
        language: &str,
        slug: &str,
    ) -> Result<Option<PageContent>> {
        // Try runtime loading first (for dynamic content)
        if let Some(content) = self
            .load_content_runtime(content_type, language, slug)
            .await?
        {
            return Ok(Some(content));
        }

        // Fall back to static file loader if available
        if let Some(ref loader) = self.file_loader {
            // Try to load from pre-built files
            return loader.load_by_slug(slug).map_err(Into::into);
        }

        Ok(None)
    }

    /// Load all content for a category with runtime processing
    #[cfg(feature = "content-static")]
    pub async fn load_category_content_runtime(
        &self,
        content_type: &str,
        language: &str,
        category: &str,
    ) -> Result<Vec<PageContent>> {
        let category_dir = self
            .content_root
            .join(content_type)
            .join(language)
            .join(category);

        if !category_dir.exists() {
            return Ok(Vec::new());
        }

        let mut contents = Vec::new();
        let mut entries = tokio::fs::read_dir(&category_dir).await?;

        while let Some(entry) = entries.next_entry().await? {
            let path = entry.path();
            if path.extension().map_or(false, |ext| ext == "md") {
                if let Some(stem) = path.file_stem() {
                    if let Some(slug) = stem.to_str() {
                        if let Some(content) = self
                            .load_content_runtime(content_type, language, slug)
                            .await?
                        {
                            contents.push(content);
                        }
                    }
                }
            }
        }

        // Sort by published date or title
        contents.sort_by(|a, b| {
            b.published_at
                .unwrap_or_default()
                .cmp(&a.published_at.unwrap_or_default())
        });

        Ok(contents)
    }

    /// Check if content exists without loading it
    #[cfg(feature = "content-static")]
    pub async fn content_exists(&self, content_type: &str, language: &str, slug: &str) -> bool {
        let file_path = self
            .content_root
            .join(content_type)
            .join(language)
            .join(format!("{}.md", slug));

        file_path.exists()
    }

    /// Get content metadata without full processing
    #[cfg(feature = "content-static")]
    pub async fn get_content_metadata(
        &self,
        content_type: &str,
        language: &str,
        slug: &str,
    ) -> Result<Option<super::processor::FrontMatter>> {
        let file_path = self
            .content_root
            .join(content_type)
            .join(language)
            .join(format!("{}.md", slug));

        if !file_path.exists() {
            return Ok(None);
        }

        let raw_content = tokio::fs::read_to_string(&file_path).await?;
        let processed = self.processor.process(&raw_content)?;

        Ok(Some(processed.frontmatter))
    }

    /// Invalidate runtime cache for specific content
    #[cfg(feature = "content-static")]
    pub async fn invalidate_runtime_cache(&self, content_type: &str, language: &str, slug: &str) {
        if self.enable_cache {
            let cache_key = format!("{}:{}:{}", content_type, language, slug);
            let mut lru_cache = self.lru_cache.write().await;
            lru_cache.pop(&cache_key);
            debug!("Invalidated runtime cache for {}", cache_key);
        }
    }

    /// Warm up cache by pre-loading popular content
    #[cfg(feature = "content-static")]
    pub async fn warm_cache(&self, content_items: Vec<(String, String, String)>) -> Result<()> {
        info!("Warming cache for {} items", content_items.len());

        for (content_type, language, slug) in content_items {
            if let Err(e) = self
                .load_content_runtime(&content_type, &language, &slug)
                .await
            {
                warn!(
                    "Failed to warm cache for {}/{}/{}: {}",
                    content_type, language, slug, e
                );
            }
        }

        Ok(())
    }

    // Index-Based Query Methods

    /// Get posts by category using pre-built indexes for O(1) lookup
    #[cfg(feature = "content-static")]
    pub async fn get_posts_by_category_indexed(
        &self,
        content_type: &str,
        language: &str,
        category: &str,
        limit: Option<usize>,
    ) -> Result<Vec<PostIndexEntry>> {
        if let Some(ref indexer) = self.indexer {
            let mut posts = indexer
                .get_category_posts(content_type, language, category)
                .await?;

            if let Some(limit) = limit {
                posts.truncate(limit);
            }

            Ok(posts)
        } else {
            warn!("Indexer not configured, falling back to file system search");
            Ok(Vec::new())
        }
    }

    /// Get posts by tag using pre-built indexes
    #[cfg(feature = "content-static")]
    pub async fn get_posts_by_tag_indexed(
        &self,
        content_type: &str,
        language: &str,
        tag: &str,
        limit: Option<usize>,
    ) -> Result<Vec<PostIndexEntry>> {
        if let Some(ref indexer) = self.indexer {
            let mut posts = indexer.get_tag_posts(content_type, language, tag).await?;

            if let Some(limit) = limit {
                posts.truncate(limit);
            }

            Ok(posts)
        } else {
            warn!("Indexer not configured, falling back to file system search");
            Ok(Vec::new())
        }
    }

    /// Search posts with full-text search using indexes
    #[cfg(feature = "content-static")]
    pub async fn search_posts_indexed(
        &self,
        content_type: &str,
        language: &str,
        query: &str,
        limit: Option<usize>,
    ) -> Result<Vec<PostIndexEntry>> {
        if let Some(ref indexer) = self.indexer {
            indexer
                .search_posts(content_type, language, query, limit)
                .await
        } else {
            warn!("Indexer not configured, cannot perform search");
            Ok(Vec::new())
        }
    }

    /// Get content index on-demand (generates if needed)
    #[cfg(feature = "content-static")]
    pub async fn get_content_index(
        &self,
        content_type: &str,
        language: &str,
    ) -> Result<super::indexer::ContentIndex> {
        if let Some(ref indexer) = self.indexer {
            indexer.get_or_generate_index(content_type, language).await
        } else {
            Err(anyhow::anyhow!("Indexer not configured"))
        }
    }

    /// Regenerate index for content type/language
    #[cfg(feature = "content-static")]
    pub async fn regenerate_index(&self, content_type: &str, language: &str) -> Result<()> {
        if let Some(ref indexer) = self.indexer {
            // Invalidate cache first
            indexer.invalidate_cache(content_type, language).await;
            // Then regenerate
            indexer.generate_index(content_type, language).await?;
            info!("Regenerated index for {}/{}", content_type, language);
            Ok(())
        } else {
            Err(anyhow::anyhow!("Indexer not configured"))
        }
    }

    /// Update index incrementally for specific content change
    #[cfg(feature = "content-static")]
    pub async fn update_index_incremental(
        &self,
        content_type: &str,
        language: &str,
        category: Option<&str>,
        post_id: Option<&str>,
    ) -> Result<()> {
        if let Some(ref indexer) = self.indexer {
            indexer
                .update_index_incremental(content_type, language, category, post_id)
                .await?;
            debug!(
                "Updated index incrementally for {}/{} (category: {:?}, post: {:?})",
                content_type, language, category, post_id
            );
            Ok(())
        } else {
            warn!("Indexer not configured, skipping incremental update");
            Ok(())
        }
    }

    /// Clear all index caches
    #[cfg(feature = "content-static")]
    pub async fn clear_index_cache(&self) {
        if let Some(ref indexer) = self.indexer {
            indexer.clear_cache().await;
            info!("Cleared index cache");
        }
    }

    /// Get index cache statistics
    #[cfg(feature = "content-static")]
    pub async fn get_index_cache_stats(&self) -> (usize, Vec<String>) {
        if let Some(ref indexer) = self.indexer {
            indexer.get_cache_stats().await
        } else {
            (0, Vec::new())
        }
    }

    /// Check if index exists and is current for given content type/language
    #[cfg(feature = "content-static")]
    pub async fn is_index_current(&self, content_type: &str, language: &str) -> bool {
        if let Some(ref indexer) = self.indexer {
            indexer.is_index_current(content_type, language).await
        } else {
            false
        }
    }
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ContentStats {
    pub total_count: i64,
    pub published_count: i64,
    pub draft_count: i64,
}

impl ContentService {
    /// Version Control API Methods

    /// Create a new version of content
    #[cfg(feature = "content-static")]
    pub async fn create_content_version(
        &self,
        content_path: &str,
        author: Option<String>,
        version_notes: Option<String>,
    ) -> Result<Option<String>> {
        if let Some(ref version_control) = self.version_control {
            // Load the current content directly from file
            if let Some(content) = self.load_content_by_path(content_path).await? {
                // Convert PageContent back to FileContent for versioning
                let file_content = self.page_content_to_file_content(&content, content_path)?;

                let version = version_control
                    .create_version(content_path, &file_content, author, version_notes)
                    .await?;

                return Ok(Some(version));
            }
        }
        Ok(None)
    }

    /// Get version history for content
    #[cfg(feature = "content-static")]
    pub async fn get_content_version_history(
        &self,
        content_path: &str,
    ) -> Result<Option<super::version_control::VersionHistory>> {
        if let Some(ref version_control) = self.version_control {
            let history = version_control.get_version_history(content_path).await?;
            return Ok(Some(history));
        }
        Ok(None)
    }

    /// Get specific version of content
    #[cfg(feature = "content-static")]
    pub async fn get_content_version(
        &self,
        content_path: &str,
        version: &str,
    ) -> Result<Option<PageContent>> {
        if let Some(ref version_control) = self.version_control {
            if let Some(file_content) = version_control.get_version(content_path, version).await? {
                return Ok(Some(file_content.into_page_content()));
            }
        }
        Ok(None)
    }

    /// Rollback content to specific version
    #[cfg(feature = "content-static")]
    pub async fn rollback_content_to_version(
        &self,
        content_path: &str,
        target_version: &str,
        author: Option<String>,
    ) -> Result<Option<String>> {
        if let Some(ref version_control) = self.version_control {
            let new_version = version_control
                .rollback_to_version(content_path, target_version, author)
                .await?;

            // Clear cache for this content
            self.clear_content_cache(content_path).await;

            return Ok(Some(new_version));
        }
        Ok(None)
    }

    /// Generate diff between versions
    #[cfg(feature = "content-static")]
    pub async fn generate_version_diff(
        &self,
        content_path: &str,
        from_version: &str,
        to_version: &str,
    ) -> Result<Option<super::version_control::VersionDiff>> {
        if let Some(ref version_control) = self.version_control {
            let diff = version_control
                .generate_diff(content_path, from_version, to_version)
                .await?;
            return Ok(Some(diff));
        }
        Ok(None)
    }

    /// List all versions for content
    #[cfg(feature = "content-static")]
    pub async fn list_content_versions(&self, content_path: &str) -> Result<Vec<String>> {
        if let Some(ref version_control) = self.version_control {
            return version_control.list_versions(content_path).await;
        }
        Ok(Vec::new())
    }

    /// Auto-version content when modified (called internally)
    /// TODO: Integrate with content update operations when CMS functionality is
    /// added
    #[cfg(feature = "content-static")]
    #[allow(dead_code)]
    async fn auto_version_if_enabled(
        &self,
        content_path: &str,
        content: &super::file_loader::FileContent,
        author: Option<String>,
    ) -> Result<()> {
        if let Some(ref version_control) = self.version_control {
            if super::version_control::utils::should_auto_version(&content.front_matter) {
                if let Some(version) = version_control
                    .auto_version_content(content_path, content, author)
                    .await?
                {
                    tracing::info!(
                        "Auto-versioned content {} to version {}",
                        content_path,
                        version
                    );
                }
            }
        }
        Ok(())
    }

    /// Helper: Convert PageContent to FileContent for versioning
    #[cfg(feature = "content-static")]
    fn page_content_to_file_content(
        &self,
        page_content: &PageContent,
        file_path: &str,
    ) -> Result<super::file_loader::FileContent> {
        use std::path::PathBuf;

        use super::file_loader::{FileContent, FrontMatter};

        let front_matter = FrontMatter {
            title: page_content.title.clone(),
            slug: Some(page_content.slug.clone()),
            name: Some(page_content.name.clone()),
            author: page_content.author.clone(),
            author_id: page_content.author_id.map(|id| id.to_string()),
            content_type: Some(page_content.content_type.as_str().to_string()),
            content_format: Some(page_content.content_format.as_str().to_string()),
            container: Some(page_content.container.clone()),
            state: Some(page_content.state.as_str().to_string()),
            require_login: Some(page_content.require_login),
            date_init: Some(page_content.date_init),
            date_end: page_content.date_end,
            published_at: page_content.published_at,
            tags: if page_content.tags.is_empty() {
                None
            } else {
                Some(page_content.tags.clone())
            },
            category: page_content.category.clone(),
            featured_image: page_content.featured_image.clone(),
            excerpt: page_content.excerpt.clone(),
            seo_title: page_content.seo_title.clone(),
            seo_description: page_content.seo_description.clone(),
            allow_comments: Some(page_content.allow_comments),
            sort_order: Some(page_content.sort_order),
            metadata: if page_content.metadata.is_empty() {
                None
            } else {
                Some(page_content.metadata.clone())
            },
            // Set defaults for fields not in PageContent
            date: None,
            read_time: None,
            difficulty: None,
            prep_time: None,
            duration: None,
            prerequisites: None,
            css_class: None,
            css_style: None,
            translations: None,
            // Version control fields - extract from metadata if present
            version: page_content.metadata.get("version").cloned(),
            version_notes: page_content.metadata.get("version_notes").cloned(),
            parent_version: page_content.metadata.get("parent_version").cloned(),
            version_created_at: None, // Could be parsed from metadata if needed
        };

        Ok(FileContent {
            front_matter,
            content: page_content.content.clone(),
            file_path: PathBuf::from(file_path),
            file_name: PathBuf::from(file_path)
                .file_name()
                .and_then(|n| n.to_str())
                .unwrap_or("unknown")
                .to_string(),
        })
    }

    /// Helper: Load content by file path (for version control operations)
    #[cfg(feature = "content-static")]
    async fn load_content_by_path(&self, content_path: &str) -> Result<Option<PageContent>> {
        let full_path = self.content_root.join(content_path);

        if !full_path.exists() {
            return Ok(None);
        }

        // Use existing file loader or create a temporary one
        let result = if let Some(ref loader) = self.file_loader {
            loader.load_file(&full_path)
        } else {
            let temp_loader = FileContentLoader::new(&self.content_root);
            temp_loader.load_file(&full_path)
        };

        match result {
            Ok(file_content) => {
                let page_content = file_content.into_page_content();
                Ok(Some(page_content))
            }
            Err(e) => {
                tracing::error!("Failed to load content from path {}: {}", content_path, e);
                Err(e)
            }
        }
    }

    /// Helper: Clear cache for specific content
    async fn clear_content_cache(&self, content_path: &str) {
        if self.enable_cache {
            let mut cache = self.cache.write().await;
            cache.remove(content_path);

            #[cfg(feature = "content-static")]
            {
                let mut lru_cache = self.lru_cache.write().await;
                lru_cache.pop(content_path);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_content_source_variants() {
        // Test that all ContentSource variants can be created
        let _database = ContentSource::Database;
        let _files = ContentSource::Files;
        let _both = ContentSource::Both;

        // Test matching
        assert!(matches!(ContentSource::Database, ContentSource::Database));
        assert!(matches!(ContentSource::Files, ContentSource::Files));
        assert!(matches!(ContentSource::Both, ContentSource::Both));
    }

    #[test]
    fn test_file_loader_creation() {
        let temp_dir = TempDir::new().unwrap();
        let loader = FileContentLoader::new(temp_dir.path());

        // Test that the loader can be created with extensions
        let loader_with_ext = loader.with_extensions(vec!["md".to_string(), "txt".to_string()]);

        // Just verify we can create the loader without errors
        assert!(true);
    }

    #[test]
    fn test_content_stats() {
        let stats = ContentStats {
            total_count: 100,
            published_count: 80,
            draft_count: 20,
        };

        assert_eq!(stats.total_count, 100);
        assert_eq!(stats.published_count, 80);
        assert_eq!(stats.draft_count, 20);
    }
}
