import pytest
from httpx import AsyncClient


async def test_get_shelves_without_auth(client: AsyncClient) -> None:
    response = await client.get("/shelves")
    assert response.status_code == 401


async def test_get_shelves_with_auth(authenticated_client: AsyncClient) -> None:
    response = await authenticated_client.get("/shelves")
    assert response.status_code == 200

    result = response.json()
    assert len(result["items"]) == 0


async def test_get_existing_shelves(
    populated_authenticated_client: AsyncClient,
) -> None:
    response = await populated_authenticated_client.get("/shelves")
    assert response.status_code == 200

    result = response.json()
    assert len(result["items"]) == 1


async def test_create_shelf(authenticated_client: AsyncClient) -> None:
    shelf_data = {"title": "Favourites"}

    response = await authenticated_client.post("/shelves", json=shelf_data)

    assert response.status_code == 201

    result = response.json()

    assert result["title"] == "Favourites"
    assert result["library_id"] is None


async def test_create_shelf_in_nonexistent_library(
    authenticated_client: AsyncClient,
) -> None:
    shelf_data = {"title": "Favourites", "library_id": 5}

    response = await authenticated_client.post("/shelves", json=shelf_data)

    assert response.status_code == 400
    assert f"Library with ID {shelf_data['library_id']} does not exist" in response.text


async def test_create_shelf_in_existing_library(
    authenticated_client: AsyncClient,
) -> None:
    shelf_data = {"title": "Favourites", "library_id": 1}

    response = await authenticated_client.post("/shelves", json=shelf_data)

    assert response.status_code == 201

    result = response.json()

    assert result["title"] == "Favourites"
    assert result["library_id"] == 1


async def test_delete_shelf_without_auth(client: AsyncClient) -> None:
    response = await client.delete("/shelves/1")
    assert response.status_code == 401


async def test_delete_shelf_unauthorized(
    authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
) -> None:
    """Verify users can't delete shelves they don't own."""
    # Create a shelf as authenticated_client
    shelf_data = {"title": "My Shelf"}
    shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
    shelf_id = shelf_response.json()["id"]

    # Try to delete as other_authenticated_client
    response = await other_authenticated_client.delete(f"/shelves/{shelf_id}")
    assert response.status_code == 403
    assert "do not have permission" in response.text


async def test_add_books_unauthorized(
    authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
) -> None:
    """Verify users can't add books to shelves they don't own."""
    # Create a shelf as authenticated_client
    shelf_data = {"title": "Other User's Shelf"}
    shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
    shelf_id = shelf_response.json()["id"]

    # Try to add books as other_authenticated_client
    response = await other_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2]}
    )
    assert response.status_code == 403
    assert "do not have permission" in response.text


async def test_remove_books_unauthorized(
    authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
) -> None:
    """Verify users can't remove books from shelves they don't own."""
    # Create a shelf and add books as authenticated_client
    shelf_data = {"title": "Other User's Shelf"}
    shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
    shelf_id = shelf_response.json()["id"]

    await authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2]}
    )

    # Try to remove books as other_authenticated_client
    response = await other_authenticated_client.delete(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
    )
    assert response.status_code == 403
    assert "do not have permission" in response.text


async def test_delete_nonexistent_shelf(authenticated_client: AsyncClient) -> None:
    """Verify 404 when deleting a shelf that doesn't exist."""
    response = await authenticated_client.delete("/shelves/99999")
    assert response.status_code == 404


async def test_add_books_to_shelf(populated_authenticated_client: AsyncClient) -> None:
    """Successfully add books to a shelf."""
    shelf_data = {"title": "Test Shelf"}
    shelf_response = await populated_authenticated_client.post(
        "/shelves", json=shelf_data
    )
    shelf_id = shelf_response.json()["id"]

    book_ids = [1, 2]
    response = await populated_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
    )
    assert response.status_code == 201

    # Verify by listing books filtered by shelf
    books_response = await populated_authenticated_client.get(
        "/books", params={"shelves": shelf_id}
    )
    assert books_response.status_code == 200
    assert len(books_response.json()["items"]) == 2


async def test_add_books_to_nonexistent_shelf(
    authenticated_client: AsyncClient,
) -> None:
    """Verify 404 when adding to nonexistent shelf."""
    response = await authenticated_client.post(
        "/shelves/99999/books", params={"book_ids": [1, 2]}
    )
    assert response.status_code == 404


async def test_add_nonexistent_books(authenticated_client: AsyncClient) -> None:
    """Verify appropriate error when book IDs don't exist."""
    shelf_data = {"title": "Test Shelf"}
    shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
    shelf_id = shelf_response.json()["id"]

    response = await authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": [99999, 99998]}
    )

    assert response.status_code == 400


async def test_remove_books_from_shelf(
    populated_authenticated_client: AsyncClient,
) -> None:
    """Successfully remove books from a shelf."""
    shelf_data = {"title": "Test Shelf"}
    shelf_response = await populated_authenticated_client.post(
        "/shelves", json=shelf_data
    )
    shelf_id = shelf_response.json()["id"]

    # Add books first
    book_ids = [1, 2, 3]
    await populated_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
    )

    # Remove one book
    response = await populated_authenticated_client.delete(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
    )
    assert response.status_code == 200

    # Verify by listing books
    books_response = await populated_authenticated_client.get(
        "/books", params={"shelves": shelf_id}
    )

    
    assert books_response.status_code == 200
    assert books_response.json()["total"] == 2


async def test_remove_books_from_nonexistent_shelf(
    authenticated_client: AsyncClient,
) -> None:
    """Verify 404 when removing from nonexistent shelf."""
    response = await authenticated_client.delete(
        "/shelves/99999/books", params={"book_ids": [1, 2]}
    )
    assert response.status_code == 404


async def test_remove_nonexistent_books(
    populated_authenticated_client: AsyncClient,
) -> None:
    """Verify appropriate error handling when removing nonexistent books."""
    shelf_data = {"title": "Test Shelf"}
    shelf_response = await populated_authenticated_client.post(
        "/shelves", json=shelf_data
    )
    shelf_id = shelf_response.json()["id"]

    # Add a book first
    await populated_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
    )

    # Try to remove books that don't exist on shelf
    response = await populated_authenticated_client.delete(
        f"/shelves/{shelf_id}/books", params={"book_ids": [99999]}
    )

    # Idempotent behaviour
    assert response.status_code == 200


async def test_add_duplicate_books(populated_authenticated_client: AsyncClient) -> None:
    """Verify behavior when adding books already on shelf."""
    shelf_data = {"title": "Test Shelf"}
    shelf_response = await populated_authenticated_client.post(
        "/shelves", json=shelf_data
    )
    shelf_id = shelf_response.json()["id"]

    # Add books
    book_ids = [1, 2]
    await populated_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
    )

    # Try to add some of the same books again
    response = await populated_authenticated_client.post(
        f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2, 3]}
    )

    assert response.status_code == 201

    # Verify final state
    books_response = await populated_authenticated_client.get(
        "/books", params={"shelves": shelf_id}
    )
    result = books_response.json()["items"]

    assert len(result) == 3


async def test_create_shelf_with_empty_title(authenticated_client: AsyncClient) -> None:
    """Verify validation rejects empty shelf titles."""
    shelf_data = {"title": ""}
    response = await authenticated_client.post("/shelves", json=shelf_data)
    assert response.status_code == 400
    assert "title" in response.text.lower()


async def test_create_shelf_with_whitespace_only_title(
    authenticated_client: AsyncClient,
) -> None:
    """Verify validation rejects whitespace-only titles."""
    shelf_data = {"title": "   "}
    response = await authenticated_client.post("/shelves", json=shelf_data)
    assert response.status_code == 400
