"""Series endpoints (section 17).""" from __future__ import annotations from typing import Literal from fastapi import APIRouter, Depends, HTTPException, Query, status from redis.asyncio import Redis from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import cached_response, not_found from app.core.database import get_db from app.core.redis import get_redis from app.core.security import enforce_rate_limit from app.schemas.tv import ( SeriesCreditsOut, SeriesDetailOut, SeriesImagesOut, SeriesListOut, SeriesSeasonsOut, SeriesVideosOut, ) from app.services import tv_service from app.services.cache import build_key router = APIRouter(prefix="/series", tags=["series"], dependencies=[Depends(enforce_rate_limit)]) @router.get("", response_model=SeriesListOut) async def list_series( genre: str | None = Query(None, description="Aurora genre id, e.g. aur_genre_..."), year: int | None = Query(None, description="First air date year"), status_: str | None = Query(None, alias="status", description="Series status, e.g. ended"), sort_by: Literal["popularity", "first_air_date", "vote_average", "title"] = Query("popularity"), sort_dir: Literal["asc", "desc"] = Query("desc"), page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=50), session: AsyncSession = Depends(get_db), ) -> SeriesListOut: try: return await tv_service.list_series( session, genre_aurora_id=genre, year=year, status=status_, sort_by=sort_by, sort_dir=sort_dir, page=page, page_size=page_size, ) except tv_service.InvalidSeriesFilterError as exc: raise HTTPException(status.HTTP_422_UNPROCESSABLE_CONTENT, str(exc)) from exc @router.get("/{aurora_id}", response_model=SeriesDetailOut) async def get_series( aurora_id: str, session: AsyncSession = Depends(get_db), redis: Redis = Depends(get_redis), ) -> SeriesDetailOut: try: return await cached_response( redis, build_key("series", aurora_id), SeriesDetailOut, lambda: tv_service.get_series_detail(session, aurora_id), ) except tv_service.SeriesNotFoundError as exc: raise not_found("series", aurora_id) from exc @router.get("/{aurora_id}/seasons", response_model=SeriesSeasonsOut) async def get_series_seasons( aurora_id: str, session: AsyncSession = Depends(get_db) ) -> SeriesSeasonsOut: try: return await tv_service.get_series_seasons(session, aurora_id) except tv_service.SeriesNotFoundError as exc: raise not_found("series", aurora_id) from exc @router.get("/{aurora_id}/credits", response_model=SeriesCreditsOut) async def get_series_credits( aurora_id: str, session: AsyncSession = Depends(get_db) ) -> SeriesCreditsOut: try: return await tv_service.get_series_credits(session, aurora_id) except tv_service.SeriesNotFoundError as exc: raise not_found("series", aurora_id) from exc @router.get("/{aurora_id}/images", response_model=SeriesImagesOut) async def get_series_images( aurora_id: str, session: AsyncSession = Depends(get_db) ) -> SeriesImagesOut: try: return await tv_service.get_series_images(session, aurora_id) except tv_service.SeriesNotFoundError as exc: raise not_found("series", aurora_id) from exc @router.get("/{aurora_id}/videos", response_model=SeriesVideosOut) async def get_series_videos( aurora_id: str, session: AsyncSession = Depends(get_db) ) -> SeriesVideosOut: try: return await tv_service.get_series_videos(session, aurora_id) except tv_service.SeriesNotFoundError as exc: raise not_found("series", aurora_id) from exc