import uuid from datetime import datetime, timezone, timedelta from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy.orm import Session from app.database import get_db from app.models import Playlist, Vote, VotingConfig router = APIRouter(prefix="/voting", tags=["voting"]) COOLDOWN_SECONDS = 10 def _voter_token(request: Request) -> str: token = request.session.get("voter_token") if not token: token = str(uuid.uuid4()) request.session["voter_token"] = token return token def _is_open(config: VotingConfig | None) -> bool: if not config or not config.is_active: return False now = datetime.now().strftime("%H:%M") return config.start_time <= now <= config.end_time def _cooldown_remaining(db: Session, voter_token: str) -> int: """Segundos restantes de cooldown. 0 si puede votar.""" last = ( db.query(Vote) .filter(Vote.voter_token == voter_token) .order_by(Vote.voted_at.desc()) .first() ) if not last: return 0 voted_at = last.voted_at if voted_at.tzinfo is None: voted_at = voted_at.replace(tzinfo=timezone.utc) elapsed = (datetime.now(timezone.utc) - voted_at).total_seconds() return max(0, int(COOLDOWN_SECONDS - elapsed)) @router.get("/status") def voting_status(request: Request, db: Session = Depends(get_db)): config = db.query(VotingConfig).first() voter_token = _voter_token(request) is_open = _is_open(config) playlists = db.query(Playlist).order_by(Playlist.created_at.desc()).all() vote_counts: dict[int, int] = { pl.id: db.query(Vote).filter(Vote.playlist_id == pl.id).count() for pl in playlists } last_vote = ( db.query(Vote) .filter(Vote.voter_token == voter_token) .order_by(Vote.voted_at.desc()) .first() ) return { "is_open": is_open, "cooldown_seconds": COOLDOWN_SECONDS, "cooldown_remaining": _cooldown_remaining(db, voter_token), "config": { "start_time": config.start_time if config else None, "end_time": config.end_time if config else None, "is_active": config.is_active if config else False, }, "my_last_vote_playlist_id": last_vote.playlist_id if last_vote else None, "playlists": [ { "id": pl.id, "spotify_id": pl.spotify_id, "spotify_type": pl.spotify_type, "name": pl.name, "image_url": pl.image_url, "emoji": pl.emoji or "", "description": pl.description or "", "votes": vote_counts.get(pl.id, 0), } for pl in playlists ], } @router.post("/vote/{playlist_id}") def cast_vote(playlist_id: int, request: Request, db: Session = Depends(get_db)): config = db.query(VotingConfig).first() if not _is_open(config): raise HTTPException(status_code=403, detail="La votación no está abierta en este momento") pl = db.query(Playlist).filter(Playlist.id == playlist_id).first() if not pl: raise HTTPException(status_code=404, detail="Playlist no encontrada") voter_token = _voter_token(request) remaining = _cooldown_remaining(db, voter_token) if remaining > 0: raise HTTPException( status_code=429, detail={"message": "Debes esperar antes de votar de nuevo", "remaining": remaining}, ) db.add(Vote( playlist_id=playlist_id, voter_token=voter_token, voted_at=datetime.now(timezone.utc), )) db.commit() return {"ok": True, "cooldown_seconds": COOLDOWN_SECONDS}