"""
Credential Management service clients — sync and async.
Covers generate, list, rotate, and revoke operations.
"""

from __future__ import annotations

from typing import Any, Callable, Coroutine, Optional

from .._request import sync_request, async_request
from ..types import (
    Credential,
    CredentialStatus,
    CredentialWithSecret,
    PaginatedCredentials,
)


class CredentialClient:
    """
    Synchronous client for the Credential Management service.

    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
        self._get_token = get_token

    def generate_credential(
        self,
        agent_id: str,
        expires_at: Optional[str] = None,
    ) -> CredentialWithSecret:
        """
        Generate a new credential for an agent.
        The ``client_secret`` is shown **once** — store it securely immediately.

        Args:
            agent_id: The agent UUID.
            expires_at: Optional ISO 8601 expiry date string.

        Returns:
            CredentialWithSecret including the one-time plain-text secret.

        Raises:
            AgentIdPError: On API or network failure.
        """
        body = {"expiresAt": expires_at} if expires_at is not None else None
        data = sync_request(
            "POST", self._base_url, f"/api/v1/agents/{agent_id}/credentials",
            self._get_token(), body=body,
        )
        return CredentialWithSecret.from_dict(data)

    def list_credentials(
        self,
        agent_id: str,
        status: Optional[CredentialStatus] = None,
        page: int = 1,
        limit: int = 20,
    ) -> PaginatedCredentials:
        """
        List credentials for an agent. Secrets are never returned in list responses.

        Args:
            agent_id: The agent UUID.
            status: Filter by credential status.
            page: Page number (1-based).
            limit: Results per page.

        Returns:
            PaginatedCredentials response.

        Raises:
            AgentIdPError: On API or network failure.
        """
        data = sync_request(
            "GET", self._base_url, f"/api/v1/agents/{agent_id}/credentials",
            self._get_token(),
            params={"status": status, "page": page, "limit": limit},
        )
        return PaginatedCredentials.from_dict(data)

    def rotate_credential(
        self, agent_id: str, credential_id: str
    ) -> CredentialWithSecret:
        """
        Rotate a credential. The same ``credential_id`` is retained; a new secret is issued.
        The old secret is immediately invalidated.
        The new ``client_secret`` is shown **once** — store it securely immediately.

        Args:
            agent_id: The agent UUID.
            credential_id: The credential UUID to rotate.

        Returns:
            CredentialWithSecret with the new one-time secret.

        Raises:
            AgentIdPError: On API or network failure.
        """
        data = sync_request(
            "POST",
            self._base_url,
            f"/api/v1/agents/{agent_id}/credentials/{credential_id}/rotate",
            self._get_token(),
        )
        return CredentialWithSecret.from_dict(data)

    def revoke_credential(
        self, agent_id: str, credential_id: str
    ) -> Credential:
        """
        Revoke a credential permanently.

        Args:
            agent_id: The agent UUID.
            credential_id: The credential UUID to revoke.

        Returns:
            The revoked Credential record.

        Raises:
            AgentIdPError: On API or network failure.
        """
        data = sync_request(
            "DELETE",
            self._base_url,
            f"/api/v1/agents/{agent_id}/credentials/{credential_id}",
            self._get_token(),
        )
        return Credential.from_dict(data)


class AsyncCredentialClient:
    """
    Asynchronous client for the Credential Management service.

    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
        self._get_token = get_token

    async def generate_credential(
        self,
        agent_id: str,
        expires_at: Optional[str] = None,
    ) -> CredentialWithSecret:
        """Generate a new credential for an agent (async)."""
        body = {"expiresAt": expires_at} if expires_at is not None else None
        data = await async_request(
            "POST", self._base_url, f"/api/v1/agents/{agent_id}/credentials",
            await self._get_token(), body=body,
        )
        return CredentialWithSecret.from_dict(data)

    async def list_credentials(
        self,
        agent_id: str,
        status: Optional[CredentialStatus] = None,
        page: int = 1,
        limit: int = 20,
    ) -> PaginatedCredentials:
        """List credentials for an agent (async)."""
        data = await async_request(
            "GET", self._base_url, f"/api/v1/agents/{agent_id}/credentials",
            await self._get_token(),
            params={"status": status, "page": page, "limit": limit},
        )
        return PaginatedCredentials.from_dict(data)

    async def rotate_credential(
        self, agent_id: str, credential_id: str
    ) -> CredentialWithSecret:
        """Rotate a credential (async)."""
        data = await async_request(
            "POST",
            self._base_url,
            f"/api/v1/agents/{agent_id}/credentials/{credential_id}/rotate",
            await self._get_token(),
        )
        return CredentialWithSecret.from_dict(data)

    async def revoke_credential(
        self, agent_id: str, credential_id: str
    ) -> Credential:
        """Revoke a credential permanently (async)."""
        data = await async_request(
            "DELETE",
            self._base_url,
            f"/api/v1/agents/{agent_id}/credentials/{credential_id}",
            await self._get_token(),
        )
        return Credential.from_dict(data)
