from __future__ import annotations

import uuid
from datetime import datetime

from pgvector.sqlalchemy import Vector
from sqlalchemy import (
    BigInteger,
    CheckConstraint,
    DateTime,
    ForeignKey,
    Integer,
    String,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.dialects.postgresql import ARRAY, JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column

from app.db.session import Base

# ── Status constants ──────────────────────────────────────────────────────────

class MediaStatus:
    PENDING = "PENDING"
    DOWNLOADING = "DOWNLOADING"
    DOWNLOADED = "DOWNLOADED"
    AI_COMPLETE = "AI_COMPLETE"
    COMPLETE = "COMPLETE"
    FAILED = "FAILED"


class LinkStatus:
    PENDING = "PENDING"
    AI_PROCESSING = "AI_PROCESSING"
    AI_COMPLETE = "AI_COMPLETE"
    COMPLETE = "COMPLETE"
    FAILED = "FAILED"


VALID_MEDIA_STATUSES = (
    MediaStatus.PENDING,
    MediaStatus.DOWNLOADING,
    MediaStatus.DOWNLOADED,
    MediaStatus.AI_COMPLETE,
    MediaStatus.COMPLETE,
    MediaStatus.FAILED,
)

VALID_LINK_STATUSES = (
    LinkStatus.PENDING,
    LinkStatus.AI_PROCESSING,
    LinkStatus.AI_COMPLETE,
    LinkStatus.COMPLETE,
    LinkStatus.FAILED,
)


# ── MediaItem ─────────────────────────────────────────────────────────────────

class MediaItem(Base):
    __tablename__ = "media_items"
    __table_args__ = (
        CheckConstraint(
            f"status IN {VALID_MEDIA_STATUSES!r}".replace("(", "(").replace(",)", ")"),
            name="ck_media_status",
        ),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    user_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
    )
    source_url: Mapped[str] = mapped_column(Text, nullable=False)
    canonical_url_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    source_platform: Mapped[str | None] = mapped_column(String(32))
    s3_key: Mapped[str | None] = mapped_column(Text, unique=True)
    s3_bucket: Mapped[str | None] = mapped_column(String(128))
    thumbnail_s3_key: Mapped[str | None] = mapped_column(Text)
    file_size_bytes: Mapped[int | None] = mapped_column(BigInteger)
    duration_seconds: Mapped[int | None] = mapped_column(Integer)
    mime_type: Mapped[str | None] = mapped_column(String(64))
    page_title: Mapped[str | None] = mapped_column(Text)
    metadata_: Mapped[dict] = mapped_column("metadata", JSONB, default=dict, server_default="{}")
    status: Mapped[str] = mapped_column(String(32), default=MediaStatus.PENDING, index=True)
    embedding: Mapped[list[float] | None] = mapped_column(Vector(1536))
    embedding_model_version: Mapped[str | None] = mapped_column(String(64))
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), index=True
    )
    updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), onupdate=func.now())

    def __repr__(self) -> str:
        return f"<MediaItem id={self.id} status={self.status}>"


# ── Link ──────────────────────────────────────────────────────────────────────

class Link(Base):
    __tablename__ = "links"
    __table_args__ = (
        CheckConstraint(
            f"status IN {VALID_LINK_STATUSES!r}".replace("(", "(").replace(",)", ")"),
            name="ck_link_status",
        ),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    user_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
    )
    url: Mapped[str] = mapped_column(Text, nullable=False)
    canonical_url_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    page_title: Mapped[str | None] = mapped_column(Text)
    og_description: Mapped[str | None] = mapped_column(Text)
    status: Mapped[str] = mapped_column(String(32), default=LinkStatus.PENDING, index=True)
    embedding: Mapped[list[float] | None] = mapped_column(Vector(1536))
    embedding_model_version: Mapped[str | None] = mapped_column(String(64))
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), index=True
    )
    updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), onupdate=func.now())

    def __repr__(self) -> str:
        return f"<Link id={self.id} url={self.url[:50]}>"


# ── AIAnalysis ────────────────────────────────────────────────────────────────

class AIAnalysis(Base):
    __tablename__ = "ai_analyses"
    __table_args__ = (
        CheckConstraint("entity_type IN ('media', 'link')", name="ck_ai_entity_type"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False, index=True)
    entity_type: Mapped[str] = mapped_column(String(16), nullable=False)
    summary: Mapped[str | None] = mapped_column(Text)
    keywords: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
    entities_json: Mapped[dict] = mapped_column(
        "entities", JSONB, default=dict, server_default='{"people":[],"places":[],"orgs":[]}'
    )
    pipeline_version: Mapped[str | None] = mapped_column(String(32))
    llm_model: Mapped[str | None] = mapped_column(String(64))
    prompt_tokens: Mapped[int | None] = mapped_column(Integer)
    completion_tokens: Mapped[int | None] = mapped_column(Integer)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )

    def __repr__(self) -> str:
        return f"<AIAnalysis entity_id={self.entity_id} type={self.entity_type}>"


# ── Tag ───────────────────────────────────────────────────────────────────────

class Tag(Base):
    __tablename__ = "tags"
    __table_args__ = (UniqueConstraint("user_id", "slug", name="uq_tags_user_slug"),)

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    user_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
    )
    name: Mapped[str] = mapped_column(String(128), nullable=False)
    slug: Mapped[str] = mapped_column(String(128), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class EntityTag(Base):
    __tablename__ = "entity_tags"
    __table_args__ = (
        CheckConstraint("entity_type IN ('media', 'link')", name="ck_entity_tag_type"),
        CheckConstraint("source IN ('ai_generated', 'user_applied')", name="ck_entity_tag_source"),
    )

    entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True)
    entity_type: Mapped[str] = mapped_column(String(16), primary_key=True)
    tag_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True
    )
    source: Mapped[str] = mapped_column(String(16), default="ai_generated")
