from __future__ import annotations

import time
from dataclasses import dataclass
from typing import Any

from .errors import AuthenticationError
from .transport import Transport


def _normalize_auth0_domain(domain: str) -> str:
    if domain.startswith("https://") or domain.startswith("http://"):
        return domain.rstrip("/")
    return f"https://{domain.rstrip('/')}"


@dataclass
class ClientCredentialsAuth:
    auth0_domain: str
    audience: str
    client_id: str
    client_secret: str

    _access_token: str | None = None
    _expires_at_epoch: float | None = None

    def get_access_token(self, transport: Transport) -> str:
        if self._access_token and self._expires_at_epoch:
            # refresh a little early to avoid clock skew.
            if time.time() < (self._expires_at_epoch - 10):
                return self._access_token

        token_url = f"{_normalize_auth0_domain(self.auth0_domain)}/oauth/token"

        response = transport.request(
            "POST",
            token_url,
            headers={"content-type": "application/x-www-form-urlencoded"},
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret,
                "audience": self.audience,
            },
        )

        if response.status_code >= 400:
            raise AuthenticationError(
                f"auth0 token request failed ({response.status_code})"
            )

        try:
            payload: dict[str, Any] = response.json()
        except Exception as exc:  # pragma: no cover
            raise AuthenticationError("auth0 token response was not json") from exc

        access_token = payload.get("access_token")
        expires_in = payload.get("expires_in")

        if not isinstance(access_token, str):
            raise AuthenticationError("auth0 token response missing access_token")

        if not isinstance(expires_in, int):
            raise AuthenticationError("auth0 token response missing expires_in")

        self._access_token = access_token
        self._expires_at_epoch = time.time() + expires_in

        return access_token
