"""Base class for all authentication flows.

Provides shared token management methods: ``get_token``, ``refresh``,
``revoke``, ``export_tokens``, and ``import_tokens``.
"""

from __future__ import annotations

from typing import Any

import httpx
import logging

from ._http import DEFAULT_EXPIRES_IN, revoke_request
from ._token_manager import TokenManager

logger = logging.getLogger("frameio.auth")


class BaseAuth:
    """Abstract base for all frameio.auth flows.

    Subclasses must initialise ``_client_id``, ``_client_secret`` (or ``None``),
    ``_token_manager``, ``_timeout``, ``_http_client``, and ``_revoke_url``
    before using the inherited methods.
    """

    _client_id: str
    _client_secret: str | None
    _token_manager: TokenManager
    _timeout: float
    _max_retries: int
    _http_client: httpx.Client | None
    _revoke_url: str | None

    # ------------------------------------------------------------------
    # Public API (shared by all flows)
    # ------------------------------------------------------------------

    def get_token(self) -> str:
        """Return a valid access token, refreshing if necessary.

        Pass this **method reference** (not a call) to the SDK::

            client = Frameio(token=auth.get_token)
        """
        return self._token_manager.get_token()

    def revoke(self) -> None:
        """Revoke both tokens server-side and clear local state.

        The access token is revoked first; server-side invalidation may take
        some time to propagate.  The refresh token is revoked next — this
        takes effect immediately and prevents new access tokens from being
        minted.  Local state is cleared last, so the SDK stops making
        requests right away.

        If no refresh token is held (e.g. S2S client-credentials flow), only
        the access token is revoked.  Both calls are best-effort.
        """
        access = self._token_manager.access_token
        refresh = self._token_manager.refresh_token
        if access:
            try:
                revoke_request(
                    access,
                    self._client_id,
                    self._client_secret,
                    timeout=self._timeout,
                    client=self._http_client,
                    revoke_url=self._revoke_url,
                )
            except Exception as exc:
                logger.warning("Access token revocation failed: %s", exc)
        if refresh:
            try:
                revoke_request(
                    refresh,
                    self._client_id,
                    self._client_secret,
                    timeout=self._timeout,
                    client=self._http_client,
                    revoke_url=self._revoke_url,
                )
            except Exception as exc:
                logger.warning("Refresh token revocation failed: %s", exc)
        self._token_manager.clear()

    def export_tokens(self) -> dict[str, Any]:
        """Export current token state for persistence."""
        return self._token_manager.export_tokens()

    def import_tokens(self, data: dict[str, Any]) -> None:
        """Restore token state from a previously exported dict."""
        self._token_manager.import_tokens(data)

    # ------------------------------------------------------------------
    # Helpers
    # ------------------------------------------------------------------

    def _store_tokens(self, result: dict[str, Any]) -> None:
        """Update the token manager from an Adobe IMS response."""
        self._token_manager.set_tokens(
            access_token=result["access_token"],
            expires_in=result.get("expires_in", DEFAULT_EXPIRES_IN),
            refresh_token=result.get("refresh_token"),
        )
