"""Watched items API endpoints""" from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import Optional from datetime import datetime from uuid import UUID from app.core.database import init_db, pool as db_pool import json router = APIRouter() class WatchedItemCreate(BaseModel): media_type: str # "movie" or "show" title: str year: Optional[int] = None country_code: str watched_at: Optional[datetime] = None notes: Optional[str] = None class WatchedItemUpdate(BaseModel): title: Optional[str] = None year: Optional[int] = None country_code: Optional[str] = None watched_at: Optional[datetime] = None notes: Optional[str] = None @router.get("/summary") async def get_watched_summary(): """Get watched items summary by country""" await init_db() if db_pool is None: raise HTTPException(status_code=503, detail="Database not available") async with db_pool.connection() as conn: async with conn.cursor() as cur: query = """ SELECT country_code, media_type, COUNT(*) as count FROM moviemap.watched_item WHERE watched_at IS NOT NULL GROUP BY country_code, media_type ORDER BY country_code, media_type """ await cur.execute(query) rows = await cur.fetchall() result = {} for row in rows: country_code, media_type, count = row if country_code not in result: result[country_code] = {} result[country_code][media_type] = count return result @router.get("") async def list_watched_items(): """List all watched items""" await init_db() if db_pool is None: raise HTTPException(status_code=503, detail="Database not available") async with db_pool.connection() as conn: async with conn.cursor() as cur: query = """ SELECT id, media_type, title, year, country_code, watched_at, notes, created_at, updated_at FROM moviemap.watched_item ORDER BY created_at DESC """ await cur.execute(query) rows = await cur.fetchall() items = [] for row in rows: items.append({ "id": str(row[0]), "media_type": row[1], "title": row[2], "year": row[3], "country_code": row[4], "watched_at": row[5].isoformat() if row[5] else None, "notes": row[6], "created_at": row[7].isoformat() if row[7] else None, "updated_at": row[8].isoformat() if row[8] else None, }) return items @router.post("") async def create_watched_item(item: WatchedItemCreate): """Create a new watched item""" await init_db() if db_pool is None: raise HTTPException(status_code=503, detail="Database not available") if item.media_type not in ["movie", "show"]: raise HTTPException(status_code=400, detail="media_type must be 'movie' or 'show'") async with db_pool.connection() as conn: async with conn.cursor() as cur: query = """ INSERT INTO moviemap.watched_item (media_type, title, year, country_code, watched_at, notes) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id """ await cur.execute( query, ( item.media_type, item.title, item.year, item.country_code, item.watched_at, item.notes, ) ) result = await cur.fetchone() await conn.commit() return {"id": str(result[0]), "status": "created"} @router.patch("/{item_id}") async def update_watched_item(item_id: UUID, item: WatchedItemUpdate): """Update a watched item""" await init_db() if db_pool is None: raise HTTPException(status_code=503, detail="Database not available") async with db_pool.connection() as conn: async with conn.cursor() as cur: # Build dynamic update query updates = [] params = [] if item.title is not None: updates.append("title = %s") params.append(item.title) if item.year is not None: updates.append("year = %s") params.append(item.year) if item.country_code is not None: updates.append("country_code = %s") params.append(item.country_code) if item.watched_at is not None: updates.append("watched_at = %s") params.append(item.watched_at) if item.notes is not None: updates.append("notes = %s") params.append(item.notes) if not updates: raise HTTPException(status_code=400, detail="No fields to update") updates.append("updated_at = NOW()") params.append(str(item_id)) query = f""" UPDATE moviemap.watched_item SET {', '.join(updates)} WHERE id = %s RETURNING id """ await cur.execute(query, params) result = await cur.fetchone() await conn.commit() if not result: raise HTTPException(status_code=404, detail="Watched item not found") return {"id": str(result[0]), "status": "updated"} @router.delete("/{item_id}") async def delete_watched_item(item_id: UUID): """Delete a watched item""" await init_db() if db_pool is None: raise HTTPException(status_code=503, detail="Database not available") async with db_pool.connection() as conn: async with conn.cursor() as cur: query = "DELETE FROM moviemap.watched_item WHERE id = %s RETURNING id" await cur.execute(query, (str(item_id),)) result = await cur.fetchone() await conn.commit() if not result: raise HTTPException(status_code=404, detail="Watched item not found") return {"id": str(result[0]), "status": "deleted"}