"""Tests for Spotify PKCE and token handling."""

import asyncio
import base64
import hashlib
import time

import httpx2
import pytest

from app.adapters.spotify.auth import (
    TokenSet,
    derive_code_challenge,
    refresh_access_token,
)
from app.adapters.spotify.session import PendingLogins


def test_code_challenge_is_unpadded_base64url_sha256() -> None:
    verifier = "a-fixed-code-verifier"
    expected = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest())

    challenge = derive_code_challenge(verifier)

    assert challenge == expected.rstrip(b"=").decode()
    assert "=" not in challenge


def test_refresh_keeps_existing_refresh_token_when_omitted() -> None:
    async def run() -> None:
        async def handler(request: httpx2.Request) -> httpx2.Response:
            assert request.url == "https://accounts.spotify.com/api/token"
            return httpx2.Response(200, json={"access_token": "new", "expires_in": 3600})

        tokens = TokenSet("old", "keep-me", time.monotonic() + 3600)
        async with httpx2.AsyncClient(transport=httpx2.MockTransport(handler)) as http:
            refreshed = await refresh_access_token(http, client_id="client", tokens=tokens)

        assert refreshed.access_token == "new"
        assert refreshed.refresh_token == "keep-me"

    asyncio.run(run())


def test_token_expiry_uses_sixty_second_skew() -> None:
    assert TokenSet("access", "refresh", time.monotonic() + 59).is_expired
    assert not TokenSet("access", "refresh", time.monotonic() + 61).is_expired


def test_pending_login_add_sweeps_expired_entries(monkeypatch: pytest.MonkeyPatch) -> None:
    current_time = 0.0
    monkeypatch.setattr(
        "app.adapters.spotify.session.time.monotonic",
        lambda: current_time,
    )
    pending_logins = PendingLogins()
    pending_logins.add("expired", "old-verifier")

    current_time = 601.0
    pending_logins.add("current", "new-verifier")

    assert set(pending_logins._entries) == {"current"}
