from datetime import datetime, timedelta, timezone from fastapi import APIRouter, Depends, Request from pydantic import BaseModel from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.auth.dependencies import get_optional_user from app.db.models import RoundParticipant, User from app.db.session import get_session from app.rounds.config import get_round_config from app.rounds.service import get_active_round router = APIRouter(prefix="/rounds", tags=["rounds"]) class CurrentRoundResponse(BaseModel): server_time: str round_id: int | None = None status: str | None = None opened_at: str | None = None closes_at: str | None = None participant_count: int = 0 bet_amount_sats: int jackpot_sats: int = 0 draw_animation_seconds: int winner_user_id: int | None = None winner_amount_sats: int | None = None draw_block_height: int | None = None draw_block_hash: str | None = None chain_tip_height: int | None = None lottery_paused: bool = False user_played: bool = False @router.get("/current", response_model=CurrentRoundResponse) async def current_round( request: Request, session: AsyncSession = Depends(get_session), user: User | None = Depends(get_optional_user), ) -> CurrentRoundResponse: config = await get_round_config(session) round_ = await get_active_round(session) listener = request.app.state.electrum_listener chain_tip_height = listener.tip_height or None if round_ is None: await session.commit() return CurrentRoundResponse( server_time=datetime.now(timezone.utc).isoformat(), bet_amount_sats=config.bet_amount_sats, draw_animation_seconds=config.draw_animation_seconds, chain_tip_height=chain_tip_height, lottery_paused=config.paused, ) participant_count = await session.scalar( select(func.count()).select_from(RoundParticipant).where(RoundParticipant.round_id == round_.id) ) or 0 opened_at = round_.opened_at.replace(tzinfo=timezone.utc) closes_at = opened_at + timedelta(seconds=config.round_duration_seconds) # Lets the frontend show the personalized win/lose reveal only to players in # this round — everyone else (not logged in, or logged in but didn't bet) # just sees the generic phase progress instead of a "non hai vinto" that # wouldn't mean anything to them. user_played = False if user is not None: user_played = ( await session.scalar( select(RoundParticipant).where( RoundParticipant.round_id == round_.id, RoundParticipant.user_id == user.id ) ) ) is not None await session.commit() # Shown to players as "jackpot": the winner's 70% share of the pool (same # split rounds/scheduler.py applies at payout time), not the full pool — # what's displayed should match what the winner actually receives. pool_amount_sats = participant_count * config.bet_amount_sats jackpot_sats = pool_amount_sats * 70 // 100 return CurrentRoundResponse( server_time=datetime.now(timezone.utc).isoformat(), round_id=round_.id, status=round_.status, opened_at=opened_at.isoformat(), closes_at=closes_at.isoformat(), participant_count=participant_count, bet_amount_sats=config.bet_amount_sats, jackpot_sats=jackpot_sats, draw_animation_seconds=config.draw_animation_seconds, winner_user_id=round_.winner_user_id, winner_amount_sats=round_.winner_amount_sats, draw_block_height=round_.draw_block_height, draw_block_hash=round_.draw_block_hash, chain_tip_height=chain_tip_height, lottery_paused=config.paused, user_played=user_played, )