"""Favorites — ulubione performerki + studia + liczenie nowych scen.

Single-user (brak users), więc API zwraca/operuje na global zbiorze. Multi-user
można dodać dorzuceniem `user_id` query/header bez breaking change.

Endpointy (performers — `/favorites/...` zostawione żeby nie łamać starego mobile):
  GET    /favorites                         — lista ulubionych performerek
  POST   /favorites/{performer_id}          — dodaj (idempotent)
  DELETE /favorites/{performer_id}          — usuń
  POST   /favorites/{performer_id}/seen     — mark-as-seen (zeruje badge)

Endpointy (studios):
  GET    /favorites/studios                 — lista ulubionych studiów
  POST   /favorites/studios/{studio_id}     — dodaj
  DELETE /favorites/studios/{studio_id}     — usuń
  POST   /favorites/studios/{studio_id}/seen — mark-as-seen

"Nowa scena" = scena której Scene.created_at > favorite.last_seen_at:
  - dla performerki: ScenePerformer.performer_id = X
  - dla studio: Scene.studio_id = X
"""
from __future__ import annotations

import uuid
from datetime import UTC, datetime
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlalchemy import func, select
from sqlalchemy.orm import Session

from app.auth import require_api_key
from app.db import get_session
from app.models.favorite_movie import FavoriteMovie
from app.models.favorite_performer import FavoritePerformer
from app.models.favorite_studio import FavoriteStudio
from app.models.movie import Movie
from app.models.performer import Performer
from app.models.playback_source import PlaybackSource
from app.models.scene import Scene, ScenePerformer
from app.models.studio import Studio

router = APIRouter(
    prefix="/favorites", tags=["favorites"], dependencies=[Depends(require_api_key)]
)


class FavoriteOut(BaseModel):
    performer_id: uuid.UUID
    canonical_name: str
    slug: str | None
    scene_count: int
    new_count: int  # sceny od last_seen_at
    last_seen_at: datetime
    created_at: datetime


class FavoriteListOut(BaseModel):
    items: list[FavoriteOut]
    total: int
    new_total: int  # suma new_count po wszystkich — dla badge w toolbar


@router.get("", response_model=FavoriteListOut)
def list_favorites(
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteListOut:
    rows = session.execute(
        select(FavoritePerformer, Performer)
        .join(Performer, Performer.id == FavoritePerformer.performer_id)
        .order_by(Performer.canonical_name)
    ).all()
    if not rows:
        return FavoriteListOut(items=[], total=0, new_total=0)

    perf_ids = [perf.id for _, perf in rows]
    last_seen_by_perf = {fav.performer_id: fav.last_seen_at for fav, _ in rows}

    # Batch: scene_count per performer — filtrujemy `has_live_playback` żeby badge
    # `N scenes` zgadzał się z tym co widać w PerformerScenes (mobile filtruje
    # `has_playback=true`). TPDB/StashDB sync wstawia metadata-only stubs które wlicz
    # by się w 2062 dla Aletta Ocean ale w profilu pokazuje tylko 499 oglądalnych.
    from sqlalchemy import and_, exists
    _scene_count_live_playback = exists().where(
        and_(
            PlaybackSource.scene_id == ScenePerformer.scene_id,
            PlaybackSource.dead_at.is_(None),
        )
    )
    scene_counts: dict = dict(
        session.execute(
            select(ScenePerformer.performer_id, func.count(ScenePerformer.scene_id))
            .where(ScenePerformer.performer_id.in_(perf_ids))
            .where(_scene_count_live_playback)
            .group_by(ScenePerformer.performer_id)
        ).all()
    )

    # Batch: new_count per performer — sceny z created_at > last_seen_at favorite'a.
    # Każda performerka ma INNY last_seen_at, więc warunek per-row. Trick: GREATEST jest
    # nieważny — robimy CASE per row z mapowaniem perf_id → last_seen przez VALUES list.
    # Prościej: jeden join + WHERE z OR po wszystkich (perf_id=X AND created_at>ts_X) —
    # ale to N OR-ów. Najczystsze rozwiązanie: zapytaj per-row ale wszystkie naraz w
    # SQL używając IN tuple lub sub-query. Tu korzystamy z faktu że N=14 typowo, więc
    # robimy unionall albo prosty (perf_id, last_seen_at) JOIN.
    new_counts: dict = {}
    if perf_ids:
        # Liczymy TYLKO sceny z żywym playback_source (has_live_playback). Powód:
        # TPDB/StashDB sync wstawia metadata-only stubs (52 scen Danielle Renae jednego
        # dnia z 0 playback) — bumpują created_at, badge `+N`, ale w PerformerScenes
        # mobile filtruje `has_playback=true` → 0 widocznych. Result: user widzi +48
        # ale w profilu nic nowego. Filter aligns count z faktycznie oglądalnym
        # contentem ("new znalezisko" = scena którą da się odtworzyć).
        from sqlalchemy import and_, exists
        live_playback = exists().where(
            and_(
                PlaybackSource.scene_id == Scene.id,
                PlaybackSource.dead_at.is_(None),
            )
        )
        per_scene_rows = session.execute(
            select(ScenePerformer.performer_id, Scene.created_at)
            .join(Scene, Scene.id == ScenePerformer.scene_id)
            .where(ScenePerformer.performer_id.in_(perf_ids))
            .where(live_playback)
        ).all()
        for pid, created_at in per_scene_rows:
            if created_at is None:
                continue
            if created_at > last_seen_by_perf.get(pid):
                new_counts[pid] = new_counts.get(pid, 0) + 1

    items: list[FavoriteOut] = []
    new_total = 0
    for fav, perf in rows:
        nc = new_counts.get(perf.id, 0)
        new_total += nc
        items.append(
            FavoriteOut(
                performer_id=perf.id,
                canonical_name=perf.canonical_name,
                slug=perf.slug,
                scene_count=scene_counts.get(perf.id, 0),
                new_count=nc,
                last_seen_at=fav.last_seen_at,
                created_at=fav.created_at,
            )
        )
    return FavoriteListOut(items=items, total=len(items), new_total=new_total)


class FavoriteAddOut(BaseModel):
    performer_id: uuid.UUID
    created: bool


@router.post(
    "/{performer_id}",
    response_model=FavoriteAddOut,
    status_code=status.HTTP_200_OK,
)
def add_favorite(
    performer_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteAddOut:
    perf = session.get(Performer, performer_id)
    if perf is None:
        raise HTTPException(status_code=404, detail="performer not found")
    existing = session.get(FavoritePerformer, performer_id)
    if existing is not None:
        return FavoriteAddOut(performer_id=performer_id, created=False)
    session.add(FavoritePerformer(performer_id=performer_id))
    session.commit()
    return FavoriteAddOut(performer_id=performer_id, created=True)


@router.delete("/{performer_id}", status_code=status.HTTP_204_NO_CONTENT)
def remove_favorite(
    performer_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> None:
    fav = session.get(FavoritePerformer, performer_id)
    if fav is None:
        # idempotent — brak ulubionego = nie ma nic do usunięcia, success
        return
    session.delete(fav)
    session.commit()


class SeenOut(BaseModel):
    performer_id: uuid.UUID
    last_seen_at: datetime


@router.post("/{performer_id}/seen", response_model=SeenOut)
def mark_seen(
    performer_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> SeenOut:
    fav = session.get(FavoritePerformer, performer_id)
    if fav is None:
        raise HTTPException(status_code=404, detail="not in favorites")
    fav.last_seen_at = datetime.now(UTC)
    session.commit()
    return SeenOut(performer_id=performer_id, last_seen_at=fav.last_seen_at)


# ---------- Studios ----------

class FavoriteStudioOut(BaseModel):
    studio_id: uuid.UUID
    name: str
    slug: str
    network: str | None = None
    scene_count: int
    new_count: int
    last_seen_at: datetime
    created_at: datetime


class FavoriteStudioListOut(BaseModel):
    items: list[FavoriteStudioOut]
    total: int
    new_total: int


@router.get("/studios", response_model=FavoriteStudioListOut)
def list_favorite_studios(
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteStudioListOut:
    rows = session.execute(
        select(FavoriteStudio, Studio)
        .join(Studio, Studio.id == FavoriteStudio.studio_id)
        .order_by(Studio.name)
    ).all()
    if not rows:
        return FavoriteStudioListOut(items=[], total=0, new_total=0)

    studio_ids = [st.id for _, st in rows]
    last_seen_by_studio = {fav.studio_id: fav.last_seen_at for fav, _ in rows}

    # has_live_playback filter — patrz `list_favorites` (performers) wyżej.
    from sqlalchemy import and_, exists
    _studio_count_live_playback = exists().where(
        and_(
            PlaybackSource.scene_id == Scene.id,
            PlaybackSource.dead_at.is_(None),
        )
    )
    scene_counts: dict = dict(
        session.execute(
            select(Scene.studio_id, func.count(Scene.id))
            .where(Scene.studio_id.in_(studio_ids))
            .where(_studio_count_live_playback)
            .group_by(Scene.studio_id)
        ).all()
    )

    new_counts: dict = {}
    if studio_ids:
        # has_live_playback filter — patrz `list_favorites` (performers) wyżej.
        from sqlalchemy import and_, exists
        live_playback = exists().where(
            and_(
                PlaybackSource.scene_id == Scene.id,
                PlaybackSource.dead_at.is_(None),
            )
        )
        per_scene_rows = session.execute(
            select(Scene.studio_id, Scene.created_at)
            .where(Scene.studio_id.in_(studio_ids))
            .where(live_playback)
        ).all()
        for sid, created_at in per_scene_rows:
            if created_at is None:
                continue
            if created_at > last_seen_by_studio.get(sid):
                new_counts[sid] = new_counts.get(sid, 0) + 1

    items: list[FavoriteStudioOut] = []
    new_total = 0
    for fav, st in rows:
        nc = new_counts.get(st.id, 0)
        new_total += nc
        items.append(
            FavoriteStudioOut(
                studio_id=st.id,
                name=st.name,
                slug=st.slug,
                network=st.network,
                scene_count=scene_counts.get(st.id, 0),
                new_count=nc,
                last_seen_at=fav.last_seen_at,
                created_at=fav.created_at,
            )
        )
    return FavoriteStudioListOut(items=items, total=len(items), new_total=new_total)


class FavoriteStudioAddOut(BaseModel):
    studio_id: uuid.UUID
    created: bool


@router.post(
    "/studios/{studio_id}",
    response_model=FavoriteStudioAddOut,
    status_code=status.HTTP_200_OK,
)
def add_favorite_studio(
    studio_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteStudioAddOut:
    st = session.get(Studio, studio_id)
    if st is None:
        raise HTTPException(status_code=404, detail="studio not found")
    existing = session.get(FavoriteStudio, studio_id)
    if existing is not None:
        return FavoriteStudioAddOut(studio_id=studio_id, created=False)
    session.add(FavoriteStudio(studio_id=studio_id))
    session.commit()
    return FavoriteStudioAddOut(studio_id=studio_id, created=True)


@router.delete("/studios/{studio_id}", status_code=status.HTTP_204_NO_CONTENT)
def remove_favorite_studio(
    studio_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> None:
    fav = session.get(FavoriteStudio, studio_id)
    if fav is None:
        return
    session.delete(fav)
    session.commit()


class SeenStudioOut(BaseModel):
    studio_id: uuid.UUID
    last_seen_at: datetime


@router.post("/studios/{studio_id}/seen", response_model=SeenStudioOut)
def mark_studio_seen(
    studio_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> SeenStudioOut:
    fav = session.get(FavoriteStudio, studio_id)
    if fav is None:
        raise HTTPException(status_code=404, detail="not in favorites")
    fav.last_seen_at = datetime.now(UTC)
    session.commit()
    return SeenStudioOut(studio_id=studio_id, last_seen_at=fav.last_seen_at)


# ── Favorite movies ────────────────────────────────────────────────────────
# Movies nie mają child scenes per-favorite (jak performerki/studia), więc
# `last_seen_at` nie jest tu używany do NEW count — tylko jako tracking ostatniego
# wglądu przez usera. Mobile używa NEW badge w liście /movies przez OSOBNY
# globalny last_seen z AsyncStorage (client-side, brak backendowego state).


class FavoriteMovieOut(BaseModel):
    movie_id: uuid.UUID
    title: str
    slug: str | None
    poster_url: str | None
    release_year: int | None
    studio_name: str | None
    last_seen_at: datetime
    created_at: datetime


class FavoriteMovieListOut(BaseModel):
    items: list[FavoriteMovieOut]
    total: int


@router.get("/movies", response_model=FavoriteMovieListOut)
def list_favorite_movies(
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteMovieListOut:
    rows = session.execute(
        select(FavoriteMovie, Movie, Studio)
        .join(Movie, Movie.id == FavoriteMovie.movie_id)
        .outerjoin(Studio, Studio.id == Movie.studio_id)
        .order_by(Movie.title)
    ).all()
    items = [
        FavoriteMovieOut(
            movie_id=movie.id,
            title=movie.title,
            slug=movie.slug,
            poster_url=movie.poster_url,
            release_year=movie.release_year,
            studio_name=studio.name if studio else None,
            last_seen_at=fav.last_seen_at,
            created_at=fav.created_at,
        )
        for fav, movie, studio in rows
    ]
    return FavoriteMovieListOut(items=items, total=len(items))


class FavoriteMovieAddOut(BaseModel):
    movie_id: uuid.UUID
    created: bool


@router.post(
    "/movies/{movie_id}",
    response_model=FavoriteMovieAddOut,
    status_code=status.HTTP_200_OK,
)
def add_favorite_movie(
    movie_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> FavoriteMovieAddOut:
    movie = session.get(Movie, movie_id)
    if movie is None:
        raise HTTPException(status_code=404, detail="movie not found")
    existing = session.get(FavoriteMovie, movie_id)
    if existing is not None:
        return FavoriteMovieAddOut(movie_id=movie_id, created=False)
    session.add(FavoriteMovie(movie_id=movie_id))
    session.commit()
    return FavoriteMovieAddOut(movie_id=movie_id, created=True)


@router.delete("/movies/{movie_id}", status_code=status.HTTP_204_NO_CONTENT)
def remove_favorite_movie(
    movie_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> None:
    fav = session.get(FavoriteMovie, movie_id)
    if fav is None:
        return
    session.delete(fav)
    session.commit()


class SeenMovieOut(BaseModel):
    movie_id: uuid.UUID
    last_seen_at: datetime


@router.post("/movies/{movie_id}/seen", response_model=SeenMovieOut)
def mark_movie_seen(
    movie_id: uuid.UUID,
    session: Annotated[Session, Depends(get_session)],
) -> SeenMovieOut:
    fav = session.get(FavoriteMovie, movie_id)
    if fav is None:
        raise HTTPException(status_code=404, detail="not in favorites")
    fav.last_seen_at = datetime.now(UTC)
    session.commit()
    return SeenMovieOut(movie_id=movie_id, last_seen_at=fav.last_seen_at)
