from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_current_active_user, get_db from app.models.rating import Rating from app.models.spot import Spot from app.models.user import User from app.schemas.common import PageResponse from app.schemas.rating import RatingCreate, RatingOut router = APIRouter() @router.post("/spots/{spot_id}/rate", response_model=RatingOut) async def rate_spot( spot_id: int, payload: RatingCreate, current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(Spot).where(Spot.id == spot_id)) spot = result.scalar_one_or_none() if not spot: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Spot not found") existing = await db.execute( select(Rating).where(Rating.user_id == current_user.id, Rating.spot_id == spot_id) ) rating = existing.scalar_one_or_none() if rating: rating.score = payload.score rating.short_comment = payload.short_comment else: rating = Rating( spot_id=spot_id, user_id=current_user.id, score=payload.score, short_comment=payload.short_comment, ) db.add(rating) await db.flush() agg = await db.execute( select(func.avg(Rating.score), func.count(Rating.id)).where(Rating.spot_id == spot_id) ) avg_score, count = agg.one() spot.avg_rating = round(float(avg_score), 2) if avg_score else None spot.rating_count = count or 0 await db.commit() await db.refresh(rating) return rating @router.get("/spots/{spot_id}/ratings", response_model=PageResponse[RatingOut]) async def list_ratings( spot_id: int, page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=100), db: AsyncSession = Depends(get_db), ): count_result = await db.execute( select(func.count(Rating.id)).where(Rating.spot_id == spot_id) ) total = count_result.scalar() or 0 offset = (page - 1) * page_size result = await db.execute( select(Rating) .where(Rating.spot_id == spot_id) .order_by(Rating.created_at.desc()) .offset(offset) .limit(page_size) ) ratings = result.scalars().all() return PageResponse(total=total, items=[RatingOut.model_validate(r) for r in ratings])