use book_manager::app::App;
use loco_rs::testing::prelude::*;
use serial_test::serial;

use super::prepare_data;

#[tokio::test]
#[serial]
async fn can_list_books_with_pagination() {
    request::<App, _, _>(|request, _ctx| async move {
        // First test if auth endpoint works (to verify routing is working)
        let auth_response = request.get("/api/auth/current").await;
        println!("Auth endpoint status: {}", auth_response.status_code());
        
        // Now test books endpoint
        let response = request.get("/api/books").await;
        
        let status = response.status_code();
        let body = response.text();
        
        println!("Books endpoint status: {}", status);
        println!("Books response (first 300 chars): {}", &body[..body.len().min(300)]);
        
        // The route should return 200 with JSON, not HTML
        assert_eq!(
            status,
            200,
            "List books request should succeed"
        );
        
        // Check if it's JSON (not HTML)
        assert!(
            body.trim_start().starts_with('{'),
            "Response should be JSON, but got: {}",
            &body[..body.len().min(100)]
        );

        // Try to parse as JSON
        let json: serde_json::Value = serde_json::from_str(&body)
            .expect("Response should be valid JSON");
        
        // Verify response structure
        assert!(json.get("books").is_some(), "Response should contain 'books' field");
        assert!(json.get("total").is_some(), "Response should contain 'total' field");
        assert!(json.get("page").is_some(), "Response should contain 'page' field");
        assert!(json.get("per_page").is_some(), "Response should contain 'per_page' field");
        assert!(json.get("total_pages").is_some(), "Response should contain 'total_pages' field");

        // Verify default values
        assert_eq!(json["page"], 1, "Default page should be 1");
        assert_eq!(json["per_page"], 12, "Default per_page should be 12");
    })
    .await;
}

#[tokio::test]
#[serial]
async fn can_list_books_with_custom_pagination() {
    request::<App, _, _>(|request, _ctx| async move {
        // Test custom pagination parameters
        let response = request.get("/api/books?page=2&per_page=5").await;
        
        assert_eq!(
            response.status_code(),
            200,
            "List books with custom pagination should succeed"
        );

        let json: serde_json::Value = response.json();
        
        assert_eq!(json["page"], 2, "Page should be 2");
        assert_eq!(json["per_page"], 5, "Per page should be 5");
    })
    .await;
}

#[tokio::test]
#[serial]
async fn can_get_single_book() {
    request::<App, _, _>(|request, ctx| async move {
        // First create a test book
        let user = prepare_data::init_user_login(&request, &ctx).await;
        
        let book_payload = serde_json::json!({
            "title": "Test Book",
            "author": "Test Author",
            "description": "A test book description",
            "content": "This is the content of the test book."
        });

        let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
        
        let create_response = request
            .post("/api/books")
            .add_header(auth_key, auth_value.clone())
            .json(&book_payload)
            .await;

        assert_eq!(
            create_response.status_code(),
            200,
            "Create book request should succeed"
        );

        let created_book: serde_json::Value = create_response.json();
        let book_id = created_book["id"].as_i64().unwrap();

        // Now get the book by ID
        let get_response = request.get(&format!("/api/books/{}", book_id)).await;
        
        assert_eq!(
            get_response.status_code(),
            200,
            "Get single book request should succeed"
        );

        let retrieved_book: serde_json::Value = get_response.json();
        
        assert_eq!(retrieved_book["title"], "Test Book");
        assert_eq!(retrieved_book["author"], "Test Author");
        assert_eq!(retrieved_book["description"], "A test book description");
    })
    .await;
}

#[tokio::test]
#[serial]
async fn can_create_book_with_auth() {
    request::<App, _, _>(|request, ctx| async move {
        let user = prepare_data::init_user_login(&request, &ctx).await;
        
        let book_payload = serde_json::json!({
            "title": "Rust Programming",
            "author": "Steve Klabnik",
            "description": "Learn Rust programming",
            "content": "Rust is a systems programming language..."
        });

        let (auth_key, auth_value) = prepare_data::auth_header(&user.token);

        let response = request
            .post("/api/books")
            .add_header(auth_key, auth_value)
            .json(&book_payload)
            .await;

        assert_eq!(
            response.status_code(),
            200,
            "Create book request should succeed with authentication"
        );

        let json: serde_json::Value = response.json();
        
        assert_eq!(json["title"], "Rust Programming");
        assert_eq!(json["author"], "Steve Klabnik");
        assert!(json.get("id").is_some(), "Created book should have an ID");
    })
    .await;
}

#[tokio::test]
#[serial]
async fn cannot_create_book_without_auth() {
    request::<App, _, _>(|request, _ctx| async move {
        let book_payload = serde_json::json!({
            "title": "Unauthorized Book",
            "author": "No Auth",
            "content": "This should fail"
        });

        let response = request
            .post("/api/books")
            .json(&book_payload)
            .await;

        // Should return 401 Unauthorized or similar error
        assert_ne!(
            response.status_code(),
            200,
            "Create book should fail without authentication"
        );
    })
    .await;
}
