//! Content Service Tests
//!
//! Comprehensive tests for the ContentService implementation including
//! caching, indexing, and content processing functionality.

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use tempfile::TempDir;
    use tokio::fs;

    use super::super::{ContentService, ContentSource};
    use crate::content::{ContentIndexer, MarkdownProcessor};

    /// Create a test content service with temporary directory
    async fn create_test_service() -> (ContentService, TempDir) {
        let temp_dir = TempDir::new().expect("Failed to create temp directory");
        let content_path = temp_dir.path().join("content");
        fs::create_dir_all(&content_path)
            .await
            .expect("Failed to create content directory");

        // Create test content structure
        let blog_dir = content_path.join("blog").join("en");
        fs::create_dir_all(&blog_dir)
            .await
            .expect("Failed to create blog directory");

        let recipes_dir = content_path.join("recipes").join("en");
        fs::create_dir_all(&recipes_dir)
            .await
            .expect("Failed to create recipes directory");

        // Create test markdown files
        let test_blog = r#"---
title: Test Blog Post
slug: test-blog-post
category: Technology
tags: ["rust", "web"]
published: true
featured: true
---

# Test Blog Post

This is a test blog post content.
"#;

        let test_recipe = r#"---
title: Test Recipe
slug: test-recipe
category: Appetizers
tags: ["quick", "easy"]
published: true
---

# Test Recipe

This is a test recipe content.

## Ingredients
- Item 1
- Item 2
"#;

        fs::write(blog_dir.join("test-blog-post.md"), test_blog)
            .await
            .expect("Failed to write test blog");

        fs::write(recipes_dir.join("test-recipe.md"), test_recipe)
            .await
            .expect("Failed to write test recipe");

        let processor = Arc::new(MarkdownProcessor::new());
        let indexer = Arc::new(ContentIndexer::new());

        let service = ContentService::new(content_path.to_str().unwrap(), processor, indexer)
            .await
            .expect("Failed to create content service");

        (service, temp_dir)
    }

    #[tokio::test]
    async fn test_content_service_creation() {
        let (service, _temp_dir) = create_test_service().await;

        // Verify service was created successfully
        assert!(service.get_cache_stats().total_entries >= 0);
    }

    #[tokio::test]
    async fn test_get_content_by_slug() {
        let (service, _temp_dir) = create_test_service().await;

        // Test getting blog content
        let blog_content = service.get_content("blog", "en", "test-blog-post").await;
        assert!(blog_content.is_ok());

        let content = blog_content.unwrap();
        assert_eq!(content.title, "Test Blog Post");
        assert_eq!(content.slug, "test-blog-post");
        assert_eq!(content.content_type, "blog");
        assert_eq!(content.language, "en");
        assert!(content.published);
        assert!(content.featured);

        // Test getting recipe content
        let recipe_content = service.get_content("recipes", "en", "test-recipe").await;
        assert!(recipe_content.is_ok());

        let content = recipe_content.unwrap();
        assert_eq!(content.title, "Test Recipe");
        assert_eq!(content.slug, "test-recipe");
        assert_eq!(content.content_type, "recipes");
    }

    #[tokio::test]
    async fn test_get_nonexistent_content() {
        let (service, _temp_dir) = create_test_service().await;

        let result = service.get_content("blog", "en", "nonexistent-slug").await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_list_content() {
        let (service, _temp_dir) = create_test_service().await;

        // List blog content
        let blog_list = service.list_content("blog", "en", None, None, None).await;
        assert!(blog_list.is_ok());

        let content_list = blog_list.unwrap();
        assert!(!content_list.is_empty());
        assert_eq!(content_list.len(), 1);
        assert_eq!(content_list[0].title, "Test Blog Post");

        // List recipes content
        let recipes_list = service
            .list_content("recipes", "en", None, None, None)
            .await;
        assert!(recipes_list.is_ok());

        let content_list = recipes_list.unwrap();
        assert!(!content_list.is_empty());
        assert_eq!(content_list.len(), 1);
        assert_eq!(content_list[0].title, "Test Recipe");
    }

    #[tokio::test]
    async fn test_search_content() {
        let (service, _temp_dir) = create_test_service().await;

        // Search for "test"
        let search_results = service.search_content("test").await;
        assert!(search_results.is_ok());

        let results = search_results.unwrap();
        assert!(results.len() >= 2); // Should find both test content items

        // Search for specific term
        let blog_search = service.search_content("blog").await;
        assert!(blog_search.is_ok());

        let results = blog_search.unwrap();
        assert!(results.iter().any(|item| item.title == "Test Blog Post"));
    }

    #[tokio::test]
    async fn test_get_content_by_category() {
        let (service, _temp_dir) = create_test_service().await;

        // Get Technology category
        let tech_content = service
            .list_content("blog", "en", Some("Technology"), None, None)
            .await;
        assert!(tech_content.is_ok());

        let content_list = tech_content.unwrap();
        assert!(!content_list.is_empty());
        assert_eq!(content_list[0].category, "Technology");

        // Get Appetizers category
        let appetizers_content = service
            .list_content("recipes", "en", Some("Appetizers"), None, None)
            .await;
        assert!(appetizers_content.is_ok());

        let content_list = appetizers_content.unwrap();
        assert!(!content_list.is_empty());
        assert_eq!(content_list[0].category, "Appetizers");
    }

    #[tokio::test]
    async fn test_get_content_by_tag() {
        let (service, _temp_dir) = create_test_service().await;

        // Get content by tag
        let rust_content = service
            .list_content("blog", "en", None, Some("rust"), None)
            .await;
        assert!(rust_content.is_ok());

        let content_list = rust_content.unwrap();
        assert!(!content_list.is_empty());
        assert!(content_list[0].tags.contains(&"rust".to_string()));
    }

    #[tokio::test]
    async fn test_featured_content() {
        let (service, _temp_dir) = create_test_service().await;

        // Get featured content
        let featured_content = service
            .list_content("blog", "en", None, None, Some(true))
            .await;
        assert!(featured_content.is_ok());

        let content_list = featured_content.unwrap();
        assert!(!content_list.is_empty());
        assert!(content_list[0].featured);
    }

    #[tokio::test]
    async fn test_cache_functionality() {
        let (service, _temp_dir) = create_test_service().await;

        // First request - should cache the content
        let start_time = std::time::Instant::now();
        let content1 = service.get_content("blog", "en", "test-blog-post").await;
        let first_duration = start_time.elapsed();
        assert!(content1.is_ok());

        // Second request - should be faster due to caching
        let start_time = std::time::Instant::now();
        let content2 = service.get_content("blog", "en", "test-blog-post").await;
        let second_duration = start_time.elapsed();
        assert!(content2.is_ok());

        // Cache hit should be faster (though this might be flaky in CI)
        // Just verify both requests succeeded
        assert_eq!(content1.unwrap().title, content2.unwrap().title);

        // Check cache stats
        let cache_stats = service.get_cache_stats();
        assert!(cache_stats.total_entries > 0);
    }

    #[tokio::test]
    async fn test_cache_invalidation() {
        let (service, _temp_dir) = create_test_service().await;

        // Load content into cache
        let _content = service.get_content("blog", "en", "test-blog-post").await;

        // Check cache has content
        let stats_before = service.get_cache_stats();
        assert!(stats_before.total_entries > 0);

        // Invalidate cache
        service.invalidate_cache().await;

        // Check cache was cleared
        let stats_after = service.get_cache_stats();
        assert_eq!(stats_after.total_entries, 0);
    }

    #[tokio::test]
    async fn test_content_reload() {
        let (mut service, temp_dir) = create_test_service().await;

        // Get initial content count
        let initial_blog_list = service
            .list_content("blog", "en", None, None, None)
            .await
            .unwrap();
        let initial_count = initial_blog_list.len();

        // Add new content file
        let new_content = r#"---
title: Another Test Post
slug: another-test-post
category: Technology
tags: ["test"]
published: true
---

# Another Test Post

This is another test post.
"#;

        let blog_dir = temp_dir.path().join("content/blog/en");
        fs::write(blog_dir.join("another-test-post.md"), new_content)
            .await
            .expect("Failed to write new content");

        // Reload content
        service
            .reload_content()
            .await
            .expect("Failed to reload content");

        // Check new content is available
        let updated_blog_list = service
            .list_content("blog", "en", None, None, None)
            .await
            .unwrap();
        assert_eq!(updated_blog_list.len(), initial_count + 1);

        // Verify new content is accessible
        let new_post = service.get_content("blog", "en", "another-test-post").await;
        assert!(new_post.is_ok());
        assert_eq!(new_post.unwrap().title, "Another Test Post");
    }

    #[tokio::test]
    async fn test_concurrent_access() {
        let (service, _temp_dir) = create_test_service().await;
        let service = Arc::new(service);

        // Create multiple concurrent tasks
        let mut handles = vec![];

        for i in 0..10 {
            let service_clone = Arc::clone(&service);
            let handle = tokio::spawn(async move {
                if i % 2 == 0 {
                    service_clone
                        .get_content("blog", "en", "test-blog-post")
                        .await
                } else {
                    service_clone
                        .get_content("recipes", "en", "test-recipe")
                        .await
                }
            });
            handles.push(handle);
        }

        // Wait for all tasks to complete
        let results: Vec<_> = futures::future::join_all(handles).await;

        // All tasks should succeed
        for result in results {
            assert!(result.is_ok());
            assert!(result.unwrap().is_ok());
        }
    }
}
