from fastapi import APIRouter, HTTPException, Request, Query from pydantic import BaseModel from typing import Optional from database import fetch_one, fetch_all, execute from auth import get_current_user from routers.utils import progress_achievement, create_notification router = APIRouter() async def _resolve_user(request: Request) -> dict: user_data = await get_current_user(request) user = await fetch_one("SELECT * FROM users WHERE user_id = ?", [str(user_data["id"])]) if not user: raise HTTPException(status_code=404, detail="User not found") return user class CreateCommentBody(BaseModel): content: str rating: Optional[int] = None class FullCommentBody(BaseModel): media_type: str tmdb_id: int content: str rating: Optional[int] = None class LikeAction(BaseModel): action: str class ReportBody(BaseModel): reason: str @router.post("/api/v1/movie/{tmdb_id}/comments") async def add_movie_comment(tmdb_id: int, body: CreateCommentBody, request: Request): user = await _resolve_user(request) await execute( "INSERT INTO comments (user_id, tmdb_id, media_type, content, rating) VALUES (?, ?, 'movie', ?, ?)", [str(user["id"]), str(tmdb_id), body.content, str(body.rating) if body.rating is not None else None], ) return {"ok": True, "data": {"created": True}} @router.get("/api/v1/movie/{tmdb_id}/comments") async def get_movie_comments(tmdb_id: int, sort: str = Query("newest", pattern="^(newest|oldest|rating)$"), page: int = Query(1, ge=1)): offset = (page - 1) * 20 order = "c.created_at DESC" if sort == "oldest": order = "c.created_at ASC" elif sort == "rating": order = "c.rating DESC, c.created_at DESC" rows = await fetch_all( f"""SELECT c.*, u.username, u.first_name, u.photo_url FROM comments c LEFT JOIN users u ON c.user_id = u.id WHERE c.tmdb_id=? AND c.media_type='movie' ORDER BY {order} LIMIT 20 OFFSET {offset}""", [str(tmdb_id)], ) total_row = await fetch_one("SELECT COUNT(*) as cnt FROM comments WHERE tmdb_id=? AND media_type='movie'", [str(tmdb_id)]) total = total_row["cnt"] if total_row else 0 return { "ok": True, "data": rows, "meta": {"page": page, "total": total, "has_more": offset + 20 < total}, } @router.post("/api/v1/series/{tmdb_id}/comments") async def add_series_comment(tmdb_id: int, body: CreateCommentBody, request: Request): user = await _resolve_user(request) await execute( "INSERT INTO comments (user_id, tmdb_id, media_type, content, rating) VALUES (?, ?, 'tv', ?, ?)", [str(user["id"]), str(tmdb_id), body.content, str(body.rating) if body.rating is not None else None], ) return {"ok": True, "data": {"created": True}} @router.get("/api/v1/series/{tmdb_id}/comments") async def get_series_comments(tmdb_id: int, sort: str = Query("newest", pattern="^(newest|oldest|rating)$"), page: int = Query(1, ge=1)): offset = (page - 1) * 20 order = "c.created_at DESC" if sort == "oldest": order = "c.created_at ASC" elif sort == "rating": order = "c.rating DESC, c.created_at DESC" rows = await fetch_all( f"""SELECT c.*, u.username, u.first_name, u.photo_url FROM comments c LEFT JOIN users u ON c.user_id = u.id WHERE c.tmdb_id=? AND c.media_type='tv' ORDER BY {order} LIMIT 20 OFFSET {offset}""", [str(tmdb_id)], ) total_row = await fetch_one("SELECT COUNT(*) as cnt FROM comments WHERE tmdb_id=? AND media_type='tv'", [str(tmdb_id)]) total = total_row["cnt"] if total_row else 0 return { "ok": True, "data": rows, "meta": {"page": page, "total": total, "has_more": offset + 20 < total}, } @router.post("/api/v1/comment") async def create_comment(body: FullCommentBody, request: Request): user = await _resolve_user(request) await execute( "INSERT INTO comments (user_id, tmdb_id, media_type, content, rating) VALUES (?, ?, ?, ?, ?)", [str(user["id"]), str(body.tmdb_id), body.media_type, body.content, str(body.rating) if body.rating is not None else None], ) return {"ok": True, "data": {"created": True}} @router.post("/api/v1/comment/{comment_id}/like") async def like_comment(comment_id: int, body: LikeAction, request: Request): user = await _resolve_user(request) if body.action == "like": await execute( "INSERT OR IGNORE INTO comment_likes (comment_id, user_id) VALUES (?, ?)", [str(comment_id), str(user["id"])], ) elif body.action == "unlike": await execute( "DELETE FROM comment_likes WHERE comment_id=? AND user_id=?", [str(comment_id), str(user["id"])], ) return {"ok": True, "data": {"liked": body.action == "like"}} @router.post("/api/v1/comment/{comment_id}/report") async def report_comment(comment_id: int, body: ReportBody, request: Request): user = await _resolve_user(request) await execute( "INSERT INTO comment_reports (comment_id, user_id, reason) VALUES (?, ?, ?)", [str(comment_id), str(user["id"]), body.reason], ) return {"ok": True, "data": {"reported": True}} @router.post("/api/v1/rating/{rating_id}/helpful") async def mark_rating_helpful(rating_id: int, request: Request): user = await _resolve_user(request) await execute( "INSERT OR IGNORE INTO rating_helpful (rating_id, user_id) VALUES (?, ?)", [str(rating_id), str(user["id"])], ) return {"ok": True, "data": {"helpful": True}}