from typing import Optional
from sqlalchemy import ForeignKey, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
from sqlalchemy.ext.orderinglist import ordering_list

from advanced_alchemy.base import BigIntAuditBase, BigIntBase

from .book import Book


class BookList(BigIntAuditBase):
    __tablename__ = "book_lists"

    library_id: Mapped[Optional[int]] = mapped_column(
        ForeignKey("libraries.id"), nullable=True
    )
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    title: Mapped[str]
    book_links: Mapped[list["BookListLink"]] = relationship(
        back_populates="book_list",
        cascade="all, delete-orphan",
        order_by="BookListLink.position",
        collection_class=ordering_list("position"),
    )
    books: AssociationProxy[list[Book]] = association_proxy(
        "book_links", "book", creator=lambda book: BookListLink(book=book)
    )

    @property
    def total(self) -> int | None:
        """Return count of books if book_links is loaded, None otherwise."""
        try:
            return len(self.book_links) if self.book_links else 0
        except Exception:
            return None


class BookListLink(BigIntBase):
    __tablename__ = "book_list_links"
    __table_args__ = (UniqueConstraint("book_id", "list_id"),)

    book_id: Mapped[int] = mapped_column(ForeignKey("books.id", ondelete="cascade"))
    list_id: Mapped[int] = mapped_column(ForeignKey("book_lists.id"))
    position: Mapped[int]

    book: Mapped[Book] = relationship(back_populates="list_links")
    book_list: Mapped[BookList] = relationship(back_populates="book_links")
