from collections.abc import Hashable
from typing import TYPE_CHECKING

from sqlalchemy import ColumnElement, ForeignKey, UniqueConstraint
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship

from advanced_alchemy.base import BigIntBase
from advanced_alchemy.mixins import UniqueMixin


if TYPE_CHECKING:
    from .book import Book


class Tag(BigIntBase, UniqueMixin):
    __tablename__ = "tags"

    name: Mapped[str] = mapped_column(unique=True, index=True)

    @classmethod
    def unique_hash(cls, name: str) -> Hashable:
        """Generate a unique hash for deduplication."""
        return name

    @classmethod
    def unique_filter(cls, name: str) -> ColumnElement[bool]:
        """SQL filter for finding existing records."""
        return cls.name == name

    def __repr__(self) -> str:
        return f"Tag({self.name!r})"


class BookTagLink(BigIntBase):
    __tablename__ = "book_tag_link"
    __table_args__ = (UniqueConstraint("book_id", "tag_id"),)

    book_id: Mapped[int] = mapped_column(ForeignKey("books.id", ondelete="cascade"))
    tag_id: Mapped[int] = mapped_column(ForeignKey("tags.id"))

    position: Mapped[int]

    book: Mapped["Book"] = relationship(back_populates="tag_links")
    tag: Mapped[Tag] = relationship()
