import threading
import time
import uuid

import requests

from app.modules.shared.gigachat.errors import GigaChatError
from app.modules.shared.gigachat.settings import GigaChatSettings


class GigaChatTokenProvider:
    def __init__(self, settings: GigaChatSettings) -> None:
        self._settings = settings
        self._lock = threading.Lock()
        self._token: str | None = None
        self._expires_at_ms: float = 0

    def get_access_token(self) -> str:
        now_ms = time.time() * 1000
        with self._lock:
            if self._token and self._expires_at_ms - 300_000 > now_ms:
                return self._token

        token, expires_at = self._fetch_token()
        with self._lock:
            self._token = token
            self._expires_at_ms = expires_at
            return token

    def _fetch_token(self) -> tuple[str, float]:
        if not self._settings.credentials:
            raise GigaChatError("GIGACHAT_TOKEN is not set")
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json",
            "Authorization": f"Basic {self._settings.credentials}",
            "RqUID": str(uuid.uuid4()),
        }
        try:
            response = requests.post(
                self._settings.auth_url,
                headers=headers,
                data=f"scope={self._settings.scope}",
                timeout=30,
                verify=self._settings.ssl_verify,
            )
        except requests.RequestException as exc:
            raise GigaChatError(f"GigaChat auth request failed: {exc}") from exc

        if response.status_code >= 400:
            raise GigaChatError(f"GigaChat auth error {response.status_code}: {response.text}")

        payload = response.json()
        token = payload.get("access_token")
        expires_at = float(payload.get("expires_at", 0))
        if not token:
            raise GigaChatError("GigaChat auth: no access_token in response")
        return token, expires_at
