"""movies kanon + bliźniacze tabele do scen

Revision ID: 0009_movies
Revises: 0008_performer_search_meta
Create Date: 2026-05-06

Schema dla full-length adult films (paradisehill + mirrory). Movies różnią się od
scen: 60-180min runtime, multi-chapter struktura, więcej metadanych (director,
year, country, rating). Performers/studios/tags reusable (te same osoby/studia
występują w scenach i w filmach).

Nowe entity_kind: 'movie'. Nowe merge_kind: 'movie'. Movie-fingerprints rzadko
istnieją (movies nie mają standardowego pHash w industry), więc fingerprint table
pomijamy — dedup pójdzie po composite key (title+year+studio+cast Jaccard).
"""
from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

revision: str = "0009_movies"
down_revision: str | None = "0008_performer_search_meta"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    # Rozszerz enumy o 'movie'
    op.execute("ALTER TYPE entity_kind ADD VALUE IF NOT EXISTS 'movie'")
    op.execute("ALTER TYPE merge_kind ADD VALUE IF NOT EXISTS 'movie'")

    op.create_table(
        "movies",
        sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
        sa.Column("title", sa.String(512), nullable=False),
        sa.Column("title_normalized", sa.String(512), nullable=False),
        sa.Column("slug", sa.String(512)),
        sa.Column("release_year", sa.Integer),
        sa.Column("release_date", sa.Date),
        sa.Column("studio_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("studios.id", ondelete="SET NULL")),
        sa.Column("director", sa.String(256)),
        sa.Column("country", sa.String(64)),
        sa.Column("duration_sec", sa.Integer),
        sa.Column("description", sa.Text),
        sa.Column("poster_url", sa.String(2048)),
        sa.Column("backdrop_url", sa.String(2048)),
        # Rating jako float (paradisehill ma like_count + rating 0-10; trzymamy
        # uśredniony rating z primary source'a, jeśli dostępny).
        sa.Column("rating", sa.Float),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
    )
    op.create_index("ix_movies_title_normalized", "movies", ["title_normalized"])
    op.execute(
        "CREATE INDEX ix_movies_title_normalized_trgm ON movies "
        "USING GIN (title_normalized gin_trgm_ops);"
    )
    op.create_index("ix_movies_release_year", "movies", ["release_year"])
    op.create_index("ix_movies_release_date", "movies", ["release_date"])
    op.create_index("ix_movies_slug", "movies", ["slug"])
    op.create_index("ix_movies_studio_id", "movies", ["studio_id"])
    op.create_index("ix_movies_studio_year", "movies", ["studio_id", "release_year"])

    op.create_table(
        "movie_external_refs",
        sa.Column("source_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("sources.id", ondelete="CASCADE"), primary_key=True),
        sa.Column("external_id", sa.String(256), primary_key=True),
        sa.Column("movie_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("movies.id", ondelete="CASCADE"), nullable=False),
        sa.Column("confidence", sa.Float, nullable=False, server_default="1.0"),
        sa.Column("url", sa.String(1024)),
        sa.Column("first_seen", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
        sa.Column("last_seen", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
    )
    op.create_index("ix_movie_external_refs_movie_id", "movie_external_refs", ["movie_id"])

    op.create_table(
        "movie_performers",
        sa.Column("movie_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True),
        sa.Column("performer_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("performers.id", ondelete="CASCADE"), primary_key=True),
        sa.Column("role", sa.String(64)),
        sa.Column("position", sa.Integer),
        sa.Column("as_alias", sa.String(256)),
    )

    op.create_table(
        "movie_tags",
        sa.Column("movie_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True),
        sa.Column("tag_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True),
        sa.Column("source_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("sources.id", ondelete="SET NULL")),
    )

    # Chaptery — opcjonalna tabela dla filmów rozbitych na sceny/segmenty
    # (paradisehill czasem ma timestamp markers, np. "Scene 1: 00:00-15:32").
    # Każdy chapter MOŻE linkować do istniejącego Scene (jeśli ta scena też jest
    # samodzielnie znana z TPDB/StashDB), albo żyje tylko jako anchor w movie.
    op.create_table(
        "movie_chapters",
        sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
        sa.Column("movie_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("movies.id", ondelete="CASCADE"), nullable=False),
        sa.Column("chapter_index", sa.Integer, nullable=False),
        sa.Column("title", sa.String(512)),
        sa.Column("start_sec", sa.Integer),
        sa.Column("end_sec", sa.Integer),
        sa.Column("scene_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("scenes.id", ondelete="SET NULL")),
        sa.UniqueConstraint("movie_id", "chapter_index", name="uq_movie_chapters_movie_id_chapter_index"),
    )
    op.create_index("ix_movie_chapters_movie_id", "movie_chapters", ["movie_id"])

    # Playback sources dla movies — analog do playback_sources, oddzielna tabela
    # bo nie chcemy mieszać scene_id/movie_id w jednym FK column. Reuse origin
    # konwencji ('paradisehill', 'psyplay:streamporn', 'wp_movies:speedporn', itp.).
    op.create_table(
        "movie_playback_sources",
        sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
        sa.Column("movie_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("movies.id", ondelete="CASCADE"), nullable=False),
        sa.Column("origin", sa.String(64), nullable=False),
        sa.Column("page_url", sa.String(2048), nullable=False),
        sa.Column("embed_url", sa.String(2048)),
        sa.Column("stream_url", sa.String(2048)),
        sa.Column("quality", sa.String(16)),
        sa.Column("duration_sec", sa.Integer),
        sa.Column("thumbnail_url", sa.String(2048)),
        sa.Column("animated_thumbnail_url", sa.String(2048)),
        sa.Column("last_seen_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
        sa.Column("dead_at", sa.DateTime(timezone=True)),
        sa.Column("dead_reason", sa.String(512)),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False),
        sa.UniqueConstraint("origin", "page_url", name="uq_movie_playback_sources_origin_page_url"),
    )
    op.create_index("ix_movie_playback_sources_movie_id", "movie_playback_sources", ["movie_id"])
    op.create_index("ix_movie_playback_sources_origin", "movie_playback_sources", ["origin"])
    op.create_index("ix_movie_playback_sources_dead_at", "movie_playback_sources", ["dead_at"])


def downgrade() -> None:
    op.drop_table("movie_playback_sources")
    op.drop_table("movie_chapters")
    op.drop_table("movie_tags")
    op.drop_table("movie_performers")
    op.drop_table("movie_external_refs")
    op.drop_table("movies")
    # Postgres nie pozwala usuwać wartości z enum-a w prosty sposób — zostawiamy
    # 'movie' w entity_kind / merge_kind. Niewielki overhead w katalogu enum-ów
    # (rząd bajtów per typ), bezpieczniejsze niż próby DROP VALUE.
