"""
Token service clients (introspect and revoke) — sync and async.
Token issuance is handled by TokenManager / AsyncTokenManager.
"""

from __future__ import annotations

from typing import Any, Callable, Coroutine

import requests
import httpx

from ..errors import AgentIdPError
from ..types import IntrospectResponse


class TokenClient:
    """
    Synchronous client for token introspection and revocation.

    Args:
        base_url: AgentIdP server base URL.
        get_token: Callable that returns a valid Bearer token.
    """

    def __init__(
        self,
        base_url: str,
        get_token: Callable[[], str],
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._get_token = get_token

    def introspect_token(self, token_to_check: str) -> IntrospectResponse:
        """
        Check whether a token is currently active.
        Always returns successfully — check ``response.active`` for validity.

        Args:
            token_to_check: The JWT string to introspect.

        Returns:
            IntrospectResponse with ``active`` field set.

        Raises:
            AgentIdPError: On API or network failure.
        """
        url = f"{self._base_url}/api/v1/token/introspect"
        try:
            response = requests.post(
                url,
                data={"token": token_to_check},
                headers={
                    "Authorization": f"Bearer {self._get_token()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                },
                timeout=30,
            )
        except requests.RequestException as exc:
            raise AgentIdPError.network_error(exc) from exc

        body = response.json()
        if not response.ok:
            raise AgentIdPError.from_api_error(body, response.status_code)
        return IntrospectResponse.from_dict(body)

    def revoke_token(self, token_to_revoke: str) -> None:
        """
        Revoke a token immediately. Idempotent (RFC 7009).

        Args:
            token_to_revoke: The JWT string to revoke.

        Raises:
            AgentIdPError: On API or network failure.
        """
        url = f"{self._base_url}/api/v1/token/revoke"
        try:
            response = requests.post(
                url,
                data={"token": token_to_revoke},
                headers={
                    "Authorization": f"Bearer {self._get_token()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                },
                timeout=30,
            )
        except requests.RequestException as exc:
            raise AgentIdPError.network_error(exc) from exc

        if not response.ok:
            body = response.json() if response.content else {}
            raise AgentIdPError.from_api_error(body, response.status_code)


class AsyncTokenClient:
    """
    Asynchronous client for token introspection and revocation.

    Args:
        base_url: AgentIdP server base URL.
        get_token: Async callable that returns a valid Bearer token.
    """

    def __init__(
        self,
        base_url: str,
        get_token: Callable[[], Coroutine[Any, Any, str]],
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._get_token = get_token

    async def introspect_token(self, token_to_check: str) -> IntrospectResponse:
        """Check whether a token is currently active (async)."""
        url = f"{self._base_url}/api/v1/token/introspect"
        token = await self._get_token()
        try:
            async with httpx.AsyncClient(timeout=30) as client:
                response = await client.post(
                    url,
                    data={"token": token_to_check},
                    headers={
                        "Authorization": f"Bearer {token}",
                        "Content-Type": "application/x-www-form-urlencoded",
                    },
                )
        except httpx.RequestError as exc:
            raise AgentIdPError.network_error(exc) from exc

        body = response.json()
        if not response.is_success:
            raise AgentIdPError.from_api_error(body, response.status_code)
        return IntrospectResponse.from_dict(body)

    async def revoke_token(self, token_to_revoke: str) -> None:
        """Revoke a token immediately — idempotent (async)."""
        url = f"{self._base_url}/api/v1/token/revoke"
        token = await self._get_token()
        try:
            async with httpx.AsyncClient(timeout=30) as client:
                response = await client.post(
                    url,
                    data={"token": token_to_revoke},
                    headers={
                        "Authorization": f"Bearer {token}",
                        "Content-Type": "application/x-www-form-urlencoded",
                    },
                )
        except httpx.RequestError as exc:
            raise AgentIdPError.network_error(exc) from exc

        if not response.is_success:
            body = response.json() if response.content else {}
            raise AgentIdPError.from_api_error(body, response.status_code)
