import json import logging from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.core.database import get_db from app.dependencies.auth import get_current_active_user from app.models.cart import CartItem from app.models.product import Product, ProductStatus from app.models.user import User from app.schemas.cart import ( CartItem as CartItemSchema, ) from app.schemas.cart import ( CartItemCreate, CartItemUpdate, CartSummary, ) router = APIRouter() logger = logging.getLogger(__name__) @router.get("/", response_model=CartSummary) async def get_cart( db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Get the current user's shopping cart. """ cart_items = db.query(CartItem).filter(CartItem.user_id == current_user.id).all() # Enhance cart items with product information items = [] total_items = 0 subtotal = 0 total_weight = 0 for item in cart_items: # Skip items with deleted products if not item.product: continue # Skip items with unavailable products if item.product.status != ProductStatus.PUBLISHED and item.product.status != ProductStatus.OUT_OF_STOCK: continue # Get primary image if available product_image = None primary_image = next((img for img in item.product.images if img.is_primary), None) if primary_image: product_image = primary_image.image_url elif item.product.images: product_image = item.product.images[0].image_url # Calculate current price and subtotal current_price = item.product.current_price item_subtotal = current_price * item.quantity # Parse custom properties custom_properties = None if item.custom_properties: try: custom_properties = json.loads(item.custom_properties) except: custom_properties = None # Create enhanced cart item cart_item = { "id": item.id, "user_id": item.user_id, "product_id": item.product_id, "quantity": item.quantity, "price_at_addition": item.price_at_addition, "custom_properties": custom_properties, "created_at": item.created_at, "updated_at": item.updated_at, "product_name": item.product.name, "product_image": product_image, "current_price": current_price, "subtotal": item_subtotal } items.append(cart_item) total_items += item.quantity subtotal += item_subtotal # Add weight if available if item.product.weight: total_weight += item.product.weight * item.quantity return { "items": items, "total_items": total_items, "subtotal": subtotal, "total_weight": total_weight if total_weight > 0 else None } @router.post("/items", response_model=CartItemSchema) async def add_cart_item( item_in: CartItemCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Add an item to the shopping cart. """ # Check if product exists and is available product = db.query(Product).filter(Product.id == item_in.product_id).first() if not product: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Product not found" ) if product.status != ProductStatus.PUBLISHED: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Product is not available for purchase" ) if product.stock_quantity < item_in.quantity: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Not enough stock available. Only {product.stock_quantity} items left." ) # Check if the item is already in the cart existing_item = db.query(CartItem).filter( CartItem.user_id == current_user.id, CartItem.product_id == item_in.product_id ).first() if existing_item: # Update quantity if item already exists new_quantity = existing_item.quantity + item_in.quantity # Check stock for the new quantity if product.stock_quantity < new_quantity: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Not enough stock available. Only {product.stock_quantity} items left." ) existing_item.quantity = new_quantity # Update custom properties if provided if item_in.custom_properties: existing_item.custom_properties = json.dumps(item_in.custom_properties) db.commit() db.refresh(existing_item) cart_item = existing_item else: # Serialize custom properties if provided custom_properties_json = None if item_in.custom_properties: custom_properties_json = json.dumps(item_in.custom_properties) # Create new cart item cart_item = CartItem( user_id=current_user.id, product_id=item_in.product_id, quantity=item_in.quantity, price_at_addition=product.current_price, custom_properties=custom_properties_json ) db.add(cart_item) db.commit() db.refresh(cart_item) # Enhance cart item with product information for response product_image = None primary_image = next((img for img in product.images if img.is_primary), None) if primary_image: product_image = primary_image.image_url elif product.images: product_image = product.images[0].image_url cart_item.product_name = product.name cart_item.product_image = product_image cart_item.current_price = product.current_price cart_item.subtotal = product.current_price * cart_item.quantity logger.info(f"Item added to cart: Product {product.name} (ID: {product.id}) for user {current_user.email}") return cart_item @router.put("/items/{item_id}", response_model=CartItemSchema) async def update_cart_item( item_id: str, item_in: CartItemUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Update a cart item. """ # Find the cart item cart_item = db.query(CartItem).filter( CartItem.id == item_id, CartItem.user_id == current_user.id ).first() if not cart_item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Cart item not found" ) # Check if product exists product = db.query(Product).filter(Product.id == cart_item.product_id).first() if not product: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Product not found" ) # Update quantity if provided if item_in.quantity is not None: # Check stock if product.stock_quantity < item_in.quantity: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Not enough stock available. Only {product.stock_quantity} items left." ) cart_item.quantity = item_in.quantity # Update custom properties if provided if item_in.custom_properties is not None: cart_item.custom_properties = json.dumps(item_in.custom_properties) db.commit() db.refresh(cart_item) # Enhance cart item with product information for response product_image = None primary_image = next((img for img in product.images if img.is_primary), None) if primary_image: product_image = primary_image.image_url elif product.images: product_image = product.images[0].image_url cart_item.product_name = product.name cart_item.product_image = product_image cart_item.current_price = product.current_price cart_item.subtotal = product.current_price * cart_item.quantity logger.info(f"Cart item updated: ID {item_id} for user {current_user.email}") return cart_item @router.delete("/items/{item_id}", response_model=dict) async def remove_cart_item( item_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Remove an item from the shopping cart. """ cart_item = db.query(CartItem).filter( CartItem.id == item_id, CartItem.user_id == current_user.id ).first() if not cart_item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Cart item not found" ) # Delete the cart item db.delete(cart_item) db.commit() logger.info(f"Cart item removed: ID {item_id} for user {current_user.email}") return {"message": "Item removed from cart successfully"} @router.delete("/", response_model=dict) async def clear_cart( db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Clear the current user's shopping cart. """ db.query(CartItem).filter(CartItem.user_id == current_user.id).delete() db.commit() logger.info(f"Cart cleared for user {current_user.email}") return {"message": "Cart cleared successfully"}