# src/chitai/controllers/shelf.py

# Standard library
from typing import Annotated
import logging

# Third-party libraries
from litestar import Controller, get, post, delete
from litestar.di import Provide
from litestar.params import Dependency
from litestar.status_codes import HTTP_403_FORBIDDEN
from litestar.exceptions import HTTPException
from advanced_alchemy.extensions.litestar.providers import create_filter_dependencies
from advanced_alchemy.service import OffsetPagination
from advanced_alchemy.service import FilterTypeT
from advanced_alchemy.exceptions import IntegrityError
from advanced_alchemy.filters import CollectionFilter
from sqlalchemy.orm import selectinload

# Local imports
from chitai.schemas.shelf import ShelfCreate, ShelfRead
from chitai.services import dependencies as deps
from chitai.services.bookshelf import ShelfService
from chitai.database import models as m

logger = logging.getLogger(__name__)


class BookshelfController(Controller):
    """Controller for managing bookshelf operations."""

    path = "/shelves"
    dependencies = {
        "shelf_service": Provide(deps.provide_shelf_service)
    } | create_filter_dependencies(
        {"id_filter": int, "pagination_type": "limit_offset", "search": "title"}
    )

    @get()
    async def list_shelves(
        self,
        shelf_service: ShelfService,
        current_user: m.User,
        libraries: list[int] | None = None,
        filters: Annotated[list[FilterTypeT], Dependency(skip_validation=True)] = [],
    ) -> OffsetPagination[ShelfRead]:
        """
        List bookshelves for the authenticated user.

        Retrieves all shelves created by the current user, with optional filtering
        and search by shelf title.

        Query Parameters:
            library_id: Optional library ID to filter shelves (currently unused).
            id_filter: Filter by shelf ID (from create_filter_dependencies).
            search: Search term for shelf title (case-insensitive).
            limit: Number of results per page (pagination).
            offset: Number of results to skip (pagination).

        Injected Dependencies:
            shelf_service: The shelf service for database operations.
            current_user: The authenticated user making the request.
            filters: Pre-constructed filters from dependencies.

        Returns:
            Paginated list of the user's bookshelves.
        """

        if libraries:
            filters.append(CollectionFilter("library_id", values=libraries))
        filters.append(m.BookList.user_id == current_user.id)

        results, total = await shelf_service.list_and_count(
            *filters, load=[selectinload(m.BookList.book_links)]
        )
        return shelf_service.to_schema(results, total, filters, schema_type=ShelfRead)

    @post()
    async def create_shelf(
        self, data: ShelfCreate, current_user: m.User, shelf_service: ShelfService
    ) -> ShelfRead:
        """
        Create a new bookshelf for the authenticated user.

        Request Body:
            data: Shelf creation data including title and optional description.

        Injected Dependencies:
            current_user: The authenticated user creating the shelf.
            shelf_service: The shelf service for database operations.

        Returns:
            The created shelf as a ShelfRead schema.
        """

        try:
            shelf = await shelf_service.create(
                data.model_dump() | {"user_id": current_user.id}
            )

            await shelf_service.add_books(shelf.id, data.book_ids)
            return shelf_service.to_schema(shelf, schema_type=ShelfRead)

        except IntegrityError:
            raise HTTPException(
                status_code=400,
                detail=f"Library with ID {data.library_id} does not exist",
            )

    @delete("{shelf_id:int}")
    async def delete_shelf(
        self, shelf_id: int, current_user: m.User, shelf_service: ShelfService
    ) -> None:
        """
        Delete a bookshelf.

        Removes a shelf and all its associations. Only the shelf owner can delete it.

        Path Parameters:
            shelf_id: The ID of the shelf to delete.

        Injected Dependencies:
            current_user: The authenticated user (for authorization).
            shelf_service: The shelf service for database operations.

        Raises:
            HTTPException (403): If the user does not own the shelf.
            HTTPException (404): If the shelf does not exist.
        """
        shelf = await shelf_service.get(shelf_id)

        if shelf.user_id != current_user.id:
            logger.warning(
                f"Unauthorized deletion attempt: user {current_user.id} attempted to delete shelf {shelf_id}"
            )
            raise HTTPException(
                status_code=HTTP_403_FORBIDDEN,
                detail="You do not have permission to delete this shelf",
            )

        await shelf_service.delete(shelf_id)

    @post("{shelf_id:int}/books")
    async def add_books_to_shelf(
        self,
        shelf_id: int,
        book_ids: list[int],
        current_user: m.User,
        shelf_service: ShelfService,
    ) -> ShelfRead:
        """
        Add books to a bookshelf.

        Adds one or more books to an existing shelf. Only the shelf owner can add books.

        Path Parameters:
            shelf_id: The ID of the shelf.

        Query Parameters:
            book_ids: List of book IDs to add to the shelf.

        Injected Dependencies:
            current_user: The authenticated user (for authorization).
            shelf_service: The shelf service for database operations.

        Returns:
            the updated shelf

        Raises:
            HTTPException (403): If the user does not own the shelf.
            HTTPException (404): If the shelf or any book does not exist.
        """

        shelf = await shelf_service.get(shelf_id)

        if shelf.user_id != current_user.id:
            logger.warning(
                f"Unauthorized add attempt: user {current_user.id} attempted to modify shelf {shelf_id}"
            )
            raise HTTPException(
                status_code=HTTP_403_FORBIDDEN,
                detail="You do not have permission to modify this shelf",
            )

        try:
            bookshelf = await shelf_service.add_books(shelf_id, book_ids)

        except ValueError:
            raise HTTPException(
                status_code=400, detail="Attempted to add books that do not exist."
            )

        logger.debug(
            f"Added {len(book_ids)} books to shelf {shelf_id} by user {current_user.id}"
        )

        return shelf_service.to_schema(bookshelf, schema_type=ShelfRead)

    @delete("{shelf_id:int}/books", status_code=200)
    async def remove_books_from_shelf(
        self,
        shelf_id: int,
        book_ids: list[int],
        current_user: m.User,
        shelf_service: ShelfService,
    ) -> ShelfRead:
        """
        Remove books from a bookshelf.

        Removes one or more books from a shelf. Only the shelf owner can remove books.

        Path Parameters:
            shelf_id: The ID of the shelf.

        Query Parameters:
            book_ids: List of book IDs to remove from the shelf.

        Injected Dependencies:
            current_user: The authenticated user (for authorization).
            shelf_service: The shelf service for database operations.

        Returns:
            the updated shelf

        Raises:
            HTTPException (403): If the user does not own the shelf.
            HTTPException (404): If the shelf or any book does not exist.
        """
        shelf = await shelf_service.get(shelf_id)
        if shelf.user_id != current_user.id:
            logger.warning(
                f"Unauthorized remove attempt: user {current_user.id} attempted to modify shelf {shelf_id}"
            )
            raise HTTPException(
                status_code=HTTP_403_FORBIDDEN,
                detail="You do not have permission to modify this shelf",
            )

        bookshelf = await shelf_service.remove_books(shelf_id, book_ids)
        logger.debug(
            f"Removed {len(book_ids)} books from shelf {shelf_id} by user {current_user.id}"
        )

        return shelf_service.to_schema(bookshelf, schema_type=ShelfRead)
