"""In-memory Spotify login and session state."""

import asyncio
import secrets
import time
from dataclasses import dataclass, field

from app.adapters.spotify.auth import TokenSet

PENDING_LOGIN_LIFETIME_SECONDS = 600.0


@dataclass
class SpotifySession:
    """Authenticated Spotify identity and refresh coordination state."""

    tokens: TokenSet
    account_id: str
    display_name: str
    refresh_generation: int = 0
    refresh_lock: asyncio.Lock = field(default_factory=asyncio.Lock)


class SessionStore:
    """Store Spotify sessions behind opaque cookie-safe identifiers."""

    def __init__(self) -> None:
        """Create an empty session store."""
        self._sessions: dict[str, SpotifySession] = {}

    def create(self, session: SpotifySession) -> str:
        """Store a session and return its opaque identifier."""
        session_id = secrets.token_urlsafe(32)
        self._sessions[session_id] = session
        return session_id

    def get(self, session_id: str) -> SpotifySession | None:
        """Return a session by identifier when present."""
        return self._sessions.get(session_id)

    def remove(self, session_id: str) -> None:
        """Remove a session if it exists."""
        self._sessions.pop(session_id, None)


class PendingLogins:
    """Store short-lived, single-use PKCE verifiers by OAuth state."""

    def __init__(self) -> None:
        """Create an empty pending-login store."""
        self._entries: dict[str, tuple[str, float]] = {}

    def add(self, state: str, code_verifier: str) -> None:
        """Store a PKCE verifier for a newly issued OAuth state."""
        now = time.monotonic()
        expired_states = (
            pending_state
            for pending_state, (_, created_at) in self._entries.items()
            if now - created_at >= PENDING_LOGIN_LIFETIME_SECONDS
        )
        for expired_state in tuple(expired_states):
            del self._entries[expired_state]
        self._entries[state] = (code_verifier, now)

    def pop(self, state: str) -> str | None:
        """Consume a verifier unless its OAuth state is unknown or expired."""
        entry = self._entries.pop(state, None)
        if entry is None:
            return None

        code_verifier, created_at = entry
        if time.monotonic() - created_at >= PENDING_LOGIN_LIFETIME_SECONDS:
            return None
        return code_verifier
