import json from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session from core.db import get_db from core.redis_client import redis_client from core.security import get_current_user_id from .models import Cart, CartItem router = APIRouter() class AddItemIn(BaseModel): product_id: int qty: int = 1 class UpdateItemIn(BaseModel): qty: int class CartItemOut(BaseModel): product_id: int qty: int name: dict = {} price: float = 0.0 image_url: str = "" line_total: float = 0.0 class CartOut(BaseModel): items: list[CartItemOut] subtotal: float def _get_or_create_cart(db: Session, user_id: int) -> Cart: cart = db.query(Cart).filter_by(user_id=user_id).first() if not cart: cart = Cart(user_id=user_id) db.add(cart) db.commit() db.refresh(cart) return cart def _cart_to_out(cart: Cart) -> CartOut: items: list[CartItemOut] = [] subtotal = 0.0 for it in cart.items: raw = redis_client.get(f"product:{it.product_id}") if not raw: continue p = json.loads(raw) line = round(float(p["price"]) * it.qty, 2) subtotal += line items.append( CartItemOut( product_id=it.product_id, qty=it.qty, name=p.get("name", {}), price=float(p["price"]), image_url=p.get("image_url", ""), line_total=line, ) ) return CartOut(items=items, subtotal=round(subtotal, 2)) @router.get("", response_model=CartOut) def get_cart(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)): return _cart_to_out(_get_or_create_cart(db, user_id)) @router.post("/items", response_model=CartOut) def add_item( body: AddItemIn, user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db), ): if body.qty < 1: raise HTTPException(400, "qty must be >= 1") if not redis_client.get(f"product:{body.product_id}"): raise HTTPException(404, "Product not found or inactive") cart = _get_or_create_cart(db, user_id) existing = db.query(CartItem).filter_by(cart_id=cart.id, product_id=body.product_id).first() if existing: existing.qty += body.qty else: db.add(CartItem(cart_id=cart.id, product_id=body.product_id, qty=body.qty)) db.commit() db.refresh(cart) return _cart_to_out(cart) @router.put("/items/{product_id}", response_model=CartOut) def update_item( product_id: int, body: UpdateItemIn, user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db), ): cart = _get_or_create_cart(db, user_id) item = db.query(CartItem).filter_by(cart_id=cart.id, product_id=product_id).first() if not item: raise HTTPException(404, "Not in cart") if body.qty < 1: db.delete(item) else: item.qty = body.qty db.commit() db.refresh(cart) return _cart_to_out(cart) @router.delete("/items/{product_id}", response_model=CartOut) def remove_item( product_id: int, user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db), ): cart = _get_or_create_cart(db, user_id) item = db.query(CartItem).filter_by(cart_id=cart.id, product_id=product_id).first() if item: db.delete(item) db.commit() db.refresh(cart) return _cart_to_out(cart) @router.delete("", response_model=CartOut) def clear_cart(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)): cart = _get_or_create_cart(db, user_id) for it in list(cart.items): db.delete(it) db.commit() db.refresh(cart) return _cart_to_out(cart)