"""Tests for the Book search & discovery endpoints."""

import pytest
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient

from apps.books.models import Book, ReadingStatus


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def api_client():
    return APIClient()


@pytest.fixture
def user(django_user_model):
    return django_user_model.objects.create_user(
        email="reader@example.com",
        password="testpass123",
    )


@pytest.fixture
def auth_client(api_client, user):
    api_client.force_authenticate(user=user)
    return api_client


@pytest.fixture
def books():
    books_data = [
        Book.objects.create(
            title="Dune",
            author="Frank Herbert",
            genre="Science Fiction",
            reading_status=ReadingStatus.FINISHED,
            total_pages=688,
            description="A desert planet saga.",
        ),
        Book.objects.create(
            title="Neuromancer",
            author="William Gibson",
            genre="Science Fiction",
            reading_status=ReadingStatus.READING,
            total_pages=271,
            description="Cyberpunk classic.",
        ),
        Book.objects.create(
            title="The Hobbit",
            author="J.R.R. Tolkien",
            genre="Fantasy",
            reading_status=ReadingStatus.WANT_TO_READ,
            total_pages=310,
            description="A hobbit's adventure.",
        ),
        Book.objects.create(
            title="1984",
            author="George Orwell",
            genre="Dystopian",
            reading_status=ReadingStatus.FINISHED,
            total_pages=328,
            description="Big Brother is watching.",
        ),
    ]
    return books_data


# ---------------------------------------------------------------------------
# Search tests
# ---------------------------------------------------------------------------


@pytest.mark.django_db
class TestBookSearch:
    """Verify the search endpoint returns correct results."""

    def test_search_by_title(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "Dune"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "Dune" in titles
        assert "Neuromancer" not in titles

    def test_search_by_author(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "Tolkien"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "The Hobbit" in titles

    def test_search_by_genre(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "Fantasy"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "The Hobbit" in titles

    def test_search_case_insensitive(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "dune"})
        assert response.status_code == status.HTTP_200_OK
        assert any(b["title"] == "Dune" for b in response.data["results"])

    def test_search_partial_match(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "Neu"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "Neuromancer" in titles

    def test_search_empty_query_returns_all(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": ""})
        assert response.status_code == status.HTTP_200_OK
        assert len(response.data["results"]) == 4

    def test_search_no_results(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "zzzznotfound"})
        assert response.status_code == status.HTTP_200_OK
        assert len(response.data["results"]) == 0


# ---------------------------------------------------------------------------
# Filter tests
# ---------------------------------------------------------------------------


@pytest.mark.django_db
class TestBookFilters:
    """Verify filters for genre, author, and reading_status."""

    def test_filter_by_genre(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"genre": "Science Fiction"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "Dune" in titles
        assert "Neuromancer" in titles
        assert "The Hobbit" not in titles

    def test_filter_by_author(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"author": "George Orwell"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "1984" in titles
        assert "Dune" not in titles

    def test_filter_by_reading_status(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"reading_status": ReadingStatus.FINISHED})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "Dune" in titles
        assert "1984" in titles
        assert "Neuromancer" not in titles
        assert "The Hobbit" not in titles

    def test_filter_combined_with_search(self, auth_client, books):
        """Search + filter should intersect results."""
        url = reverse("book-list")
        response = auth_client.get(url, {"q": "Dune", "reading_status": ReadingStatus.FINISHED})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert "Dune" in titles
        # 1984 matches reading_status but not search
        assert "1984" not in titles


# ---------------------------------------------------------------------------
# Discovery endpoints
# ---------------------------------------------------------------------------


@pytest.mark.django_db
class TestBookDiscovery:
    """Verify genre and author discovery endpoints."""

    def test_genres_endpoint(self, auth_client, books):
        url = reverse("book-genres")
        response = auth_client.get(url)
        assert response.status_code == status.HTTP_200_OK
        assert isinstance(response.data, list)
        assert "Science Fiction" in response.data
        assert "Fantasy" in response.data
        assert "Dystopian" in response.data
        # No duplicate genres
        assert response.data.count("Science Fiction") == 1

    def test_authors_endpoint(self, auth_client, books):
        url = reverse("book-authors")
        response = auth_client.get(url)
        assert response.status_code == status.HTTP_200_OK
        assert isinstance(response.data, list)
        assert "Frank Herbert" in response.data
        assert "J.R.R. Tolkien" in response.data

    def test_genres_requires_auth(self, api_client, books):
        url = reverse("book-genres")
        response = api_client.get(url)
        assert response.status_code == status.HTTP_401_UNAUTHORIZED

    def test_authors_requires_auth(self, api_client, books):
        url = reverse("book-authors")
        response = api_client.get(url)
        assert response.status_code == status.HTTP_401_UNAUTHORIZED


# ---------------------------------------------------------------------------
# Detail view
# ---------------------------------------------------------------------------


@pytest.mark.django_db
class TestBookDetail:
    """Verify the book detail endpoint."""

    def test_retrieve_book(self, auth_client, books):
        book = books[0]
        url = reverse("book-detail", kwargs={"pk": book.pk})
        response = auth_client.get(url)
        assert response.status_code == status.HTTP_200_OK
        assert response.data["title"] == "Dune"
        assert response.data["author"] == "Frank Herbert"
        assert response.data["description"] == "A desert planet saga."
        assert response.data["total_pages"] == 688

    def test_retrieve_nonexistent_returns_404(self, auth_client, books):
        url = reverse("book-detail", kwargs={"pk": 99999})
        response = auth_client.get(url)
        assert response.status_code == status.HTTP_404_NOT_FOUND

    def test_pagination(self, auth_client, books):
        """List with small page size should paginate."""
        url = reverse("book-list")
        response = auth_client.get(f"{url}?page_size=2")
        assert response.status_code == status.HTTP_200_OK
        assert "count" in response.data
        assert "results" in response.data
        assert response.data["count"] == 4

    def test_ordering(self, auth_client, books):
        url = reverse("book-list")
        response = auth_client.get(url, {"ordering": "title"})
        assert response.status_code == status.HTTP_200_OK
        titles = [b["title"] for b in response.data["results"]]
        assert titles == sorted(titles)