from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, desc from pydantic import BaseModel from decimal import Decimal from typing import Optional from datetime import datetime from app.database import get_db from app.models.user import User from app.models.bet import Bet from app.models.bankroll import Bankroll from app.utils.auth import get_current_user from app.services.gamification import check_achievements router = APIRouter(prefix="/api/bets", tags=["bets"]) class BetCreate(BaseModel): sport: str event_name: str platform: str = "Bet365" amount: float odds: float bet_type: str = "" emotion: str = "" is_impulsive: bool = False class BetResult(BaseModel): result: str # win, loss, void @router.post("") async def create_bet(req: BetCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)): bet = Bet( user_id=user.id, sport=req.sport, event_name=req.event_name, platform=req.platform, amount=req.amount, odds=req.odds, bet_type=req.bet_type, emotion=req.emotion, is_impulsive=req.is_impulsive, result="pending" ) db.add(bet) # Update bankroll spending br = (await db.execute(select(Bankroll).where(Bankroll.user_id == user.id))).scalar_one_or_none() if br: br.month_spent = float(br.month_spent or 0) + req.amount br.week_spent = float(br.week_spent or 0) + req.amount br.day_spent = float(br.day_spent or 0) + req.amount await db.commit() await db.refresh(bet) await check_achievements(user.id, db) return {"id": str(bet.id), "status": "created"} @router.get("") async def list_bets( sport: Optional[str] = None, result: Optional[str] = None, limit: int = Query(50, le=200), offset: int = 0, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): q = select(Bet).where(Bet.user_id == user.id) if sport: q = q.where(Bet.sport == sport) if result: q = q.where(Bet.result == result) q = q.order_by(desc(Bet.created_at)).offset(offset).limit(limit) rows = (await db.execute(q)).scalars().all() return [ {"id": str(b.id), "sport": b.sport, "event_name": b.event_name, "platform": b.platform, "amount": float(b.amount), "odds": float(b.odds) if b.odds else None, "result": b.result, "profit": float(b.profit) if b.profit else 0, "emotion": b.emotion, "is_impulsive": b.is_impulsive, "created_at": b.created_at.isoformat()} for b in rows ] @router.patch("/{bet_id}/result") async def update_result(bet_id: str, req: BetResult, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)): bet = (await db.execute(select(Bet).where(Bet.id == bet_id, Bet.user_id == user.id))).scalar_one_or_none() if not bet: raise HTTPException(404, "Bet not found") bet.result = req.result if req.result == "win": bet.profit = float(bet.amount) * (float(bet.odds) - 1) elif req.result == "loss": bet.profit = -float(bet.amount) else: bet.profit = 0 await db.commit() return {"id": str(bet.id), "result": bet.result, "profit": float(bet.profit)} @router.get("/stats") async def bet_stats(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)): bets = (await db.execute(select(Bet).where(Bet.user_id == user.id))).scalars().all() total = len(bets) wins = sum(1 for b in bets if b.result == "win") losses = sum(1 for b in bets if b.result == "loss") pending = sum(1 for b in bets if b.result == "pending") total_profit = sum(float(b.profit or 0) for b in bets) total_staked = sum(float(b.amount) for b in bets) win_rate = (wins / (wins + losses) * 100) if (wins + losses) > 0 else 0 roi = (total_profit / total_staked * 100) if total_staked > 0 else 0 by_sport = {} for b in bets: s = b.sport or "other" if s not in by_sport: by_sport[s] = {"count": 0, "profit": 0} by_sport[s]["count"] += 1 by_sport[s]["profit"] += float(b.profit or 0) return { "total": total, "wins": wins, "losses": losses, "pending": pending, "total_profit": round(total_profit, 2), "total_staked": round(total_staked, 2), "win_rate": round(win_rate, 1), "roi": round(roi, 1), "by_sport": by_sport }