//! Content API Tests
//!
//! Integration tests for the Content REST API endpoints.

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

    use axum::{
        body::Body,
        http::{Request, StatusCode},
        Router,
    };
    use axum_test::TestServer;
    use serde_json;
    use tempfile::TempDir;
    use tokio::fs;
    use tower::ServiceExt;

    use super::super::api::{ContentQuery, CreateContentRequest, UpdateContentRequest};
    use super::super::{create_content_api_routes, ContentApiState, ContentService};
    use crate::content::{ContentIndexer, MarkdownProcessor};

    /// Create a test server with content API
    async fn create_test_server() -> (TestServer, 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");

        // Create test markdown file
        let test_content = r#"---
title: Test Post
slug: test-post
category: Technology
tags: ["rust", "test"]
published: true
featured: false
---

# Test Post

This is test content for API testing.
"#;

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

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

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

        let state = ContentApiState {
            content_service: Arc::new(content_service),
        };

        let app = Router::new()
            .nest("/api/content", create_content_api_routes())
            .with_state(state);

        let server = TestServer::new(app).unwrap();
        (server, temp_dir)
    }

    #[tokio::test]
    async fn test_list_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.get("/api/content/blog/en").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert!(body["data"].is_array());

        let items = body["data"].as_array().unwrap();
        assert!(!items.is_empty());
        assert_eq!(items[0]["title"], "Test Post");
        assert_eq!(items[0]["slug"], "test-post");
    }

    #[tokio::test]
    async fn test_get_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.get("/api/content/blog/en/test-post").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());

        let content = &body["data"];
        assert_eq!(content["title"], "Test Post");
        assert_eq!(content["slug"], "test-post");
        assert_eq!(content["content_type"], "blog");
        assert_eq!(content["language"], "en");
        assert!(content["published"].as_bool().unwrap());
        assert!(!content["featured"].as_bool().unwrap());
    }

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

        let response = server.get("/api/content/blog/en/nonexistent").await;

        assert_eq!(response.status_code(), StatusCode::NOT_FOUND);

        let body: serde_json::Value = response.json();
        assert!(!body["success"].as_bool().unwrap());
        assert!(body["error"].is_string());
    }

    #[tokio::test]
    async fn test_search_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.get("/api/content/search?q=test").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert!(body["data"].is_array());

        let results = body["data"].as_array().unwrap();
        assert!(!results.is_empty());
        assert!(results.iter().any(|item| item["title"] == "Test Post"));
    }

    #[tokio::test]
    async fn test_create_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let new_content = CreateContentRequest {
            title: "New Test Post".to_string(),
            slug: "new-test-post".to_string(),
            content: "# New Test Post\n\nThis is a new post.".to_string(),
            content_type: "blog".to_string(),
            language: "en".to_string(),
            published: Some(false),
            draft: Some(true),
            featured: Some(false),
            tags: vec!["new".to_string(), "test".to_string()],
            category: Some("Technology".to_string()),
            excerpt: Some("This is a new test post.".to_string()),
            description: Some("A new test post description.".to_string()),
            author: Some("Test Author".to_string()),
        };

        let response = server.post("/api/content").json(&new_content).await;

        assert_eq!(response.status_code(), StatusCode::CREATED);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert_eq!(body["data"]["title"], "New Test Post");
        assert_eq!(body["data"]["slug"], "new-test-post");
    }

    #[tokio::test]
    async fn test_update_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let update_request = UpdateContentRequest {
            title: Some("Updated Test Post".to_string()),
            content: Some("# Updated Test Post\n\nThis post has been updated.".to_string()),
            published: Some(true),
            featured: Some(true),
            tags: Some(vec!["updated".to_string(), "test".to_string()]),
            category: Some("Technology".to_string()),
            excerpt: Some("Updated test post.".to_string()),
            description: Some("Updated description.".to_string()),
            author: Some("Updated Author".to_string()),
        };

        let response = server
            .put("/api/content/blog/en/test-post")
            .json(&update_request)
            .await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert_eq!(body["data"]["title"], "Updated Test Post");
        assert!(body["data"]["featured"].as_bool().unwrap());
    }

    #[tokio::test]
    async fn test_publish_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.post("/api/content/blog/en/test-post/publish").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert!(body["data"]["published"].as_bool().unwrap());
    }

    #[tokio::test]
    async fn test_unpublish_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server
            .post("/api/content/blog/en/test-post/unpublish")
            .await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());
        assert!(!body["data"]["published"].as_bool().unwrap());
    }

    #[tokio::test]
    async fn test_delete_content_endpoint() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.delete("/api/content/blog/en/test-post").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());

        // Verify content is deleted
        let get_response = server.get("/api/content/blog/en/test-post").await;

        assert_eq!(get_response.status_code(), StatusCode::NOT_FOUND);
    }

    #[tokio::test]
    async fn test_filtered_content_listing() {
        let (server, _temp_dir) = create_test_server().await;

        // Test category filter
        let response = server.get("/api/content/blog/en?category=Technology").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());

        let items = body["data"].as_array().unwrap();
        assert!(!items.is_empty());
        assert_eq!(items[0]["category"], "Technology");
    }

    #[tokio::test]
    async fn test_tag_filtering() {
        let (server, _temp_dir) = create_test_server().await;

        // Test tag filter
        let response = server.get("/api/content/blog/en?tag=rust").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());

        let items = body["data"].as_array().unwrap();
        assert!(!items.is_empty());

        let tags = items[0]["tags"].as_array().unwrap();
        assert!(tags.iter().any(|tag| tag == "rust"));
    }

    #[tokio::test]
    async fn test_featured_content_filter() {
        let (server, _temp_dir) = create_test_server().await;

        // Test featured filter
        let response = server.get("/api/content/blog/en?featured=true").await;

        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["success"].as_bool().unwrap());

        // Since our test content is not featured, should be empty
        let items = body["data"].as_array().unwrap();
        assert!(items.is_empty());
    }

    #[tokio::test]
    async fn test_invalid_content_type() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.get("/api/content/invalid-type/en").await;

        assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);

        let body: serde_json::Value = response.json();
        assert!(!body["success"].as_bool().unwrap());
        assert!(body["error"].is_string());
    }

    #[tokio::test]
    async fn test_invalid_language() {
        let (server, _temp_dir) = create_test_server().await;

        let response = server.get("/api/content/blog/invalid-lang").await;

        assert_eq!(response.status_code(), StatusCode::NOT_FOUND);

        let body: serde_json::Value = response.json();
        assert!(!body["success"].as_bool().unwrap());
        assert!(body["error"].is_string());
    }

    #[tokio::test]
    async fn test_create_content_validation() {
        let (server, _temp_dir) = create_test_server().await;

        // Test with missing required fields
        let invalid_content = serde_json::json!({
            "title": "Test",
            // missing slug, content, content_type, language
        });

        let response = server.post("/api/content").json(&invalid_content).await;

        assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);

        let body: serde_json::Value = response.json();
        assert!(!body["success"].as_bool().unwrap());
        assert!(body["error"].is_string());
    }

    #[tokio::test]
    async fn test_concurrent_api_requests() {
        let (server, _temp_dir) = create_test_server().await;

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

        for i in 0..10 {
            let server_clone = server.clone();
            let handle = tokio::spawn(async move {
                if i % 2 == 0 {
                    server_clone.get("/api/content/blog/en/test-post").await
                } else {
                    server_clone.get("/api/content/blog/en").await
                }
            });
            handles.push(handle);
        }

        // Wait for all requests to complete
        let results = futures::future::join_all(handles).await;

        // All requests should succeed
        for result in results {
            let response = result.unwrap();
            assert_eq!(response.status_code(), StatusCode::OK);
        }
    }
}
