from datetime import datetime, timedelta from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session from core.database import get_db from core.dependencies import get_current_user from core.models import Card as CardModel from core.models import Deck as DeckModel from core.models import User as UserModel router = APIRouter() def _serialize_card_public(card: CardModel) -> dict: """Card fields safe to expose on public profiles (no user_id).""" return { "id": str(card.id), "name": card.name, "image_link": card.image_link, "card_rarity": card.card_rarity, "card_type": card.card_type, "text": card.text, "attack": card.attack, "defense": card.defense, "cost": card.cost, "is_favorite": card.is_favorite, "willing_to_trade": card.willing_to_trade, } class UpdateProfileRequest(BaseModel): trade_wishlist: str @router.get("/profile") def get_profile(user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): total_games = user.wins + user.losses most_played_deck = ( db.query(DeckModel) .filter(DeckModel.user_id == user.id, DeckModel.times_played > 0) .order_by(DeckModel.times_played.desc()) .first() ) most_played_card = ( db.query(CardModel) .filter(CardModel.user_id == user.id, CardModel.times_played > 0) .order_by(CardModel.times_played.desc()) .first() ) return { "username": user.username, "email": user.email, "email_verified": user.email_verified, "created_at": user.created_at, "wins": user.wins, "losses": user.losses, "shards": user.shards, "win_rate": round((user.wins / total_games) * 100) if total_games > 0 else None, "trade_wishlist": user.trade_wishlist or "", "most_played_deck": { "name": most_played_deck.name, "times_played": most_played_deck.times_played, } if most_played_deck else None, "most_played_card": { "name": most_played_card.name, "times_played": most_played_card.times_played, "card_type": most_played_card.card_type, "card_rarity": most_played_card.card_rarity, "image_link": most_played_card.image_link, } if most_played_card else None, } @router.get("/profile/refresh-status") def refresh_status(user: UserModel = Depends(get_current_user)): if not user.last_refresh_at: return {"can_refresh": True, "next_refresh_at": None} next_refresh = user.last_refresh_at + timedelta(minutes=10) can_refresh = datetime.now() >= next_refresh return { "can_refresh": can_refresh, "next_refresh_at": next_refresh.isoformat() if not can_refresh else None, } @router.patch("/profile") def update_profile(req: UpdateProfileRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): user.trade_wishlist = req.trade_wishlist db.commit() return {"trade_wishlist": user.trade_wishlist} @router.get("/users") def search_users(q: str, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): # Require auth to prevent scraping if len(q) < 2: return [] results = ( db.query(UserModel) .filter(UserModel.username.ilike(f"%{q}%")) .limit(20) .all() ) return [ { "username": u.username, "wins": u.wins, "losses": u.losses, "win_rate": round(u.wins / (u.wins + u.losses) * 100) if (u.wins + u.losses) > 0 else 0, } for u in results ] @router.get("/users/{username}") def get_public_profile(username: str, db: Session = Depends(get_db)): user = db.query(UserModel).filter(UserModel.username == username).first() if not user: raise HTTPException(status_code=404, detail="User not found") total_games = user.wins + user.losses favorite_cards = ( db.query(CardModel) .filter(CardModel.user_id == user.id, CardModel.is_favorite == True) .order_by(CardModel.received_at.desc()) .all() ) wtt_cards = ( db.query(CardModel) .filter(CardModel.user_id == user.id, CardModel.willing_to_trade == True) .order_by(CardModel.received_at.desc()) .all() ) return { "username": user.username, "wins": user.wins, "losses": user.losses, "win_rate": round((user.wins / total_games) * 100) if total_games > 0 else None, "trade_wishlist": user.trade_wishlist or "", "last_active_at": user.last_active_at.isoformat() if user.last_active_at else None, "favorite_cards": [_serialize_card_public(c) for c in favorite_cards], "wtt_cards": [_serialize_card_public(c) for c in wtt_cards], }