from fastapi import APIRouter, Depends from sqlalchemy import func from sqlalchemy.orm import Session from datetime import datetime, timedelta, timezone from app.core.database import get_db from app.api.deps import get_current_user from app.models.user import User from app.models.wardrobe import WardrobeItem from app.models.chat import ChatMessage from app.models.tryon import TryOnTask from app.schemas.subscription import SubscriptionResponse router = APIRouter() # Free-tier caps advertised on the landing page's pricing table. FREE_WARDROBE_ITEM_LIMIT = 30 FREE_AI_RECOMMENDATIONS_PER_WEEK = 5 # AI Stylist usage is metered like an LLM API quota, expressed in "credits" # (1 credit = 1,000 tokens) over a rolling 30-day window. TOKENS_PER_CREDIT = 1_000 FREE_AI_CREDIT_LIMIT_PER_30D = 500 def _build_response(db: Session, user: User) -> SubscriptionResponse: is_premium = user.plan == "premium" wardrobe_used = ( db.query(WardrobeItem).filter(WardrobeItem.user_id == user.id).count() ) # "AI outfits" = virtual try-on generations (each one produces one AI-styled outfit). week_ago = datetime.now(timezone.utc) - timedelta(days=7) ai_used = ( db.query(TryOnTask) .filter( TryOnTask.user_id == user.id, TryOnTask.created_at >= week_ago, ) .count() ) month_ago = datetime.now(timezone.utc) - timedelta(days=30) tokens_used = ( db.query(func.coalesce(func.sum(ChatMessage.tokens_used), 0)) .filter( ChatMessage.user_id == user.id, ChatMessage.role == "ai", ChatMessage.created_at >= month_ago, ) .scalar() ) # Round up: any partial usage still consumes a whole credit, like real LLM billing. credits_used = -(-int(tokens_used or 0) // TOKENS_PER_CREDIT) return SubscriptionResponse( plan=user.plan, renews_at=user.premium_renews_at, wardrobe_items_used=wardrobe_used, wardrobe_items_limit=None if is_premium else FREE_WARDROBE_ITEM_LIMIT, ai_recommendations_used=ai_used, ai_recommendations_limit=None if is_premium else FREE_AI_RECOMMENDATIONS_PER_WEEK, ai_credits_used=credits_used, ai_credits_limit=None if is_premium else FREE_AI_CREDIT_LIMIT_PER_30D, ) @router.get("/me", response_model=SubscriptionResponse) def get_my_subscription( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): return _build_response(db, current_user) # There is no self-serve payment flow yet, so upgrading to Premium is not exposed here — # only accounts granted Premium manually (direct DB update) can downgrade themselves back. @router.post("/cancel", response_model=SubscriptionResponse) def cancel_my_subscription( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): current_user.plan = "free" current_user.premium_renews_at = None db.commit() db.refresh(current_user) return _build_response(db, current_user)