from pydantic import BaseModel, Field, ConfigDict from typing import List, Optional from datetime import datetime, timezone import uuid def _now_iso() -> str: return datetime.now(timezone.utc).isoformat() # ---------- Users ---------- class UserCreate(BaseModel): email: str password: str name: str class UserLogin(BaseModel): email: str password: str class UserPublic(BaseModel): model_config = ConfigDict(extra="ignore") id: str email: str name: str is_admin: bool = False created_at: str # ---------- Movies ---------- class MovieBase(BaseModel): title: str description: str = "" year: int = 2024 duration_minutes: int = 0 rating: str = "NR" # G / PG / PG-13 / R / NR genres: List[str] = [] cast: List[str] = [] director: str = "" poster_url: str = "" backdrop_url: str = "" video_url: str = "" storage_type: str = "external" # "external" or "local" storage_path: Optional[str] = None featured: bool = False class MovieCreate(MovieBase): pass class MovieUpdate(BaseModel): title: Optional[str] = None description: Optional[str] = None year: Optional[int] = None duration_minutes: Optional[int] = None rating: Optional[str] = None genres: Optional[List[str]] = None cast: Optional[List[str]] = None director: Optional[str] = None poster_url: Optional[str] = None backdrop_url: Optional[str] = None video_url: Optional[str] = None storage_type: Optional[str] = None storage_path: Optional[str] = None featured: Optional[bool] = None class Movie(MovieBase): model_config = ConfigDict(extra="ignore") id: str = Field(default_factory=lambda: str(uuid.uuid4())) created_at: str = Field(default_factory=_now_iso) # ---------- Watchlist ---------- class WatchlistItem(BaseModel): model_config = ConfigDict(extra="ignore") id: str = Field(default_factory=lambda: str(uuid.uuid4())) user_id: str movie_id: str added_at: str = Field(default_factory=_now_iso) # ---------- Progress (Continue Watching) ---------- class ProgressUpsert(BaseModel): movie_id: str position_seconds: float duration_seconds: float class Progress(BaseModel): model_config = ConfigDict(extra="ignore") user_id: str movie_id: str position_seconds: float duration_seconds: float updated_at: str = Field(default_factory=_now_iso) # ---------- Requests ---------- class RequestCreate(BaseModel): title: str year: Optional[int] = None notes: str = "" class RequestUpdate(BaseModel): status: str # pending | fulfilled | rejected class MovieRequest(BaseModel): model_config = ConfigDict(extra="ignore") id: str = Field(default_factory=lambda: str(uuid.uuid4())) user_id: str user_name: str = "" title: str year: Optional[int] = None notes: str = "" status: str = "pending" created_at: str = Field(default_factory=_now_iso) # ---------- Auth Tokens ---------- class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" user: UserPublic