import logging from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import desc from sqlalchemy.orm import Session from app.core.database import get_db from app.dependencies.auth import get_current_active_user from app.models.order import Order, OrderStatus from app.models.product import Product from app.models.review import Review from app.models.user import User, UserRole from app.schemas.review import ( Review as ReviewSchema, ) from app.schemas.review import ( ReviewCreate, ReviewUpdate, ) router = APIRouter() logger = logging.getLogger(__name__) @router.get("/", response_model=list[ReviewSchema]) async def get_reviews( product_id: str | None = None, user_id: str | None = None, approved_only: bool = True, skip: int = 0, limit: int = 100, db: Session = Depends(get_db), ): """ Get reviews. Filter by product_id or user_id. Regular users can only see approved reviews for products. """ query = db.query(Review) # Filter by product ID if provided if product_id: query = query.filter(Review.product_id == product_id) # Filter by user ID if provided if user_id: query = query.filter(Review.user_id == user_id) # Filter by approved status if approved_only: query = query.filter(Review.is_approved == True) # Apply pagination and order by newest first reviews = query.order_by(desc(Review.created_at)).offset(skip).limit(limit).all() # Enhance reviews with user and product names for review in reviews: # Add user name if available if review.user and review.user.first_name: if review.user.last_name: review.user_name = f"{review.user.first_name} {review.user.last_name}" else: review.user_name = review.user.first_name else: review.user_name = "Anonymous" # Add product name if available if review.product: review.product_name = review.product.name return reviews @router.get("/{review_id}", response_model=ReviewSchema) async def get_review( review_id: str, db: Session = Depends(get_db), ): """ Get a specific review by ID. """ review = db.query(Review).filter(Review.id == review_id).first() if not review: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Review not found" ) # Add user name if available if review.user and review.user.first_name: if review.user.last_name: review.user_name = f"{review.user.first_name} {review.user.last_name}" else: review.user_name = review.user.first_name else: review.user_name = "Anonymous" # Add product name if available if review.product: review.product_name = review.product.name return review @router.post("/", response_model=ReviewSchema) async def create_review( review_in: ReviewCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Create a new review for a product. User must have purchased the product to leave a verified review. """ # Check if product exists product = db.query(Product).filter(Product.id == review_in.product_id).first() if not product: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Product not found" ) # Check if user has already reviewed this product existing_review = db.query(Review).filter( Review.product_id == review_in.product_id, Review.user_id == current_user.id ).first() if existing_review: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="You have already reviewed this product" ) # Check if the user has purchased the product for verified review status is_verified_purchase = False # Query completed orders completed_orders = db.query(Order).filter( Order.user_id == current_user.id, Order.status.in_([OrderStatus.DELIVERED, OrderStatus.COMPLETED]) ).all() # Check if any of these orders contain the product for order in completed_orders: for item in order.items: if item.product_id == review_in.product_id: is_verified_purchase = True break if is_verified_purchase: break # Admin reviews are always approved, regular user reviews may need approval is_approved = True if current_user.role == UserRole.ADMIN else True # Auto-approve for now # Create the review db_review = Review( product_id=review_in.product_id, user_id=current_user.id, rating=review_in.rating, title=review_in.title, comment=review_in.comment, is_verified_purchase=is_verified_purchase, is_approved=is_approved ) db.add(db_review) db.commit() db.refresh(db_review) # Add user name and product name for the response if current_user.first_name: if current_user.last_name: db_review.user_name = f"{current_user.first_name} {current_user.last_name}" else: db_review.user_name = current_user.first_name else: db_review.user_name = "Anonymous" db_review.product_name = product.name logger.info(f"Review created: ID {db_review.id} for product {product.name}") return db_review @router.put("/{review_id}", response_model=ReviewSchema) async def update_review( review_id: str, review_in: ReviewUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Update a review. Users can only update their own reviews. Admins can update any review. """ review = db.query(Review).filter(Review.id == review_id).first() if not review: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Review not found" ) # Check permissions is_admin = current_user.role == UserRole.ADMIN is_owner = review.user_id == current_user.id if not is_admin and not is_owner: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions to update this review" ) # Regular users cannot update approval status update_data = review_in.dict(exclude_unset=True) if not is_admin and "is_approved" in update_data: del update_data["is_approved"] # Update review attributes for key, value in update_data.items(): setattr(review, key, value) db.commit() db.refresh(review) # Add user name and product name for the response if review.user and review.user.first_name: if review.user.last_name: review.user_name = f"{review.user.first_name} {review.user.last_name}" else: review.user_name = review.user.first_name else: review.user_name = "Anonymous" if review.product: review.product_name = review.product.name logger.info(f"Review updated: ID {review.id}") return review @router.delete("/{review_id}", response_model=dict) async def delete_review( review_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Delete a review. Users can only delete their own reviews. Admins can delete any review. """ review = db.query(Review).filter(Review.id == review_id).first() if not review: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Review not found" ) # Check permissions is_admin = current_user.role == UserRole.ADMIN is_owner = review.user_id == current_user.id if not is_admin and not is_owner: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions to delete this review" ) db.delete(review) db.commit() logger.info(f"Review deleted: ID {review.id}") return {"message": "Review successfully deleted"}