import uuid
from datetime import UTC, datetime

from sqlalchemy import BigInteger, DateTime, ForeignKey, Index, Integer, String, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


def _utcnow() -> datetime:
    return datetime.now(UTC)


class Image(Base):
    __tablename__ = "images"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    hash: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True)
    filename: Mapped[str] = mapped_column(String, nullable=False)
    mime_type: Mapped[str] = mapped_column(String(20), nullable=False)
    size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False)
    width: Mapped[int] = mapped_column(Integer, nullable=False)
    height: Mapped[int] = mapped_column(Integer, nullable=False)
    short_id: Mapped[str | None] = mapped_column(String(8), unique=True, nullable=True, index=True)
    storage_key: Mapped[str] = mapped_column(String(64), nullable=False)
    thumbnail_key: Mapped[str | None] = mapped_column(String(70), nullable=True, default=None)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    image_tags: Mapped[list["ImageTag"]] = relationship(
        back_populates="image", cascade="all, delete-orphan"
    )

    @property
    def tags(self) -> list[str]:
        return [it.tag.name for it in self.image_tags if it.tag]


class Tag(Base):
    __tablename__ = "tags"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    name: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    image_tags: Mapped[list["ImageTag"]] = relationship(back_populates="tag")


class ImageTag(Base):
    __tablename__ = "image_tags"
    __table_args__ = (
        UniqueConstraint("image_id", "tag_id", name="uq_image_tag"),
        Index("ix_image_tags_image_id", "image_id"),
        Index("ix_image_tags_tag_id", "tag_id"),
    )

    image_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("images.id", ondelete="CASCADE"), primary_key=True
    )
    tag_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("tags.id", ondelete="RESTRICT"), primary_key=True
    )

    image: Mapped["Image"] = relationship(back_populates="image_tags")
    tag: Mapped["Tag"] = relationship(back_populates="image_tags")
