"""
ID Wispera Provisioning Provider Interface
Base interface and abstract class for provisioning providers
"""

from abc import ABC, abstractmethod
from typing import Any, Optional

from id_wispera.types import VisaType, Platform
from id_wispera.provisioning.types import (
    ProviderCapabilities,
    ProviderAuth,
    ProvisioningRequest,
    ProvisioningResult,
    RotationResult,
)


class ProvisioningProviderBase(ABC):
    """
    Base abstract class for provisioning providers.
    All providers must implement initialize and provision.
    Optional methods: list_keys, rotate, revoke, validate_auth.
    """

    @property
    @abstractmethod
    def provider(self) -> str:
        """Provider identifier."""
        ...

    @property
    @abstractmethod
    def capabilities(self) -> ProviderCapabilities:
        """What this provider can do."""
        ...

    def __init__(self) -> None:
        self._auth: Optional[ProviderAuth] = None
        self._initialized = False

    def initialize(self, auth: ProviderAuth) -> None:
        """
        Initialize with auth credentials.

        Args:
            auth: Provider authentication configuration
        """
        self._auth = auth
        self._initialized = True

    def _ensure_initialized(self) -> None:
        """Ensure the provider has been initialized."""
        if not self._initialized or not self._auth:
            raise RuntimeError(
                f"Provider {self.provider} not initialized. Call initialize() first."
            )

    @abstractmethod
    def provision(self, request: ProvisioningRequest) -> ProvisioningResult:
        """
        Provision a new credential.

        Args:
            request: The provisioning request

        Returns:
            ProvisioningResult with the outcome
        """
        ...

    def list_keys(self) -> list[dict[str, Any]]:
        """
        List existing keys (if supported).

        Returns:
            List of dicts with id, name, createdAt, status
        """
        raise NotImplementedError(f"Provider {self.provider} does not support listing keys")

    def rotate(self, existing_key_id: str, request: ProvisioningRequest) -> RotationResult:
        """
        Rotate a credential (create new, return old ID for revocation).

        Args:
            existing_key_id: The ID of the key to rotate
            request: Provisioning request for the new key

        Returns:
            RotationResult with new credential and old key info
        """
        raise NotImplementedError(f"Provider {self.provider} does not support rotation")

    def revoke(self, key_id: str) -> dict[str, Any]:
        """
        Revoke/delete a credential at the source.

        Args:
            key_id: The key ID to revoke

        Returns:
            Dict with 'success' and optional 'error' keys
        """
        raise NotImplementedError(f"Provider {self.provider} does not support revocation")

    def validate_auth(self) -> dict[str, Any]:
        """
        Validate auth credentials without making changes.

        Returns:
            Dict with 'valid' and optional 'error' keys
        """
        raise NotImplementedError(f"Provider {self.provider} does not support auth validation")

    @staticmethod
    def map_to_visa_type(provider: str, has_elevated_permissions: bool) -> str:
        """
        Map provider-specific credential to passport visa type.

        Args:
            provider: Provider name
            has_elevated_permissions: Whether the credential has elevated permissions

        Returns:
            VisaType value string
        """
        # AI inference APIs default to 'privilege' (LLM access = elevated)
        if provider in ("openai", "anthropic"):
            return "privilege"
        # Cloud infrastructure depends on permission level
        if provider in ("aws", "google-cloud", "azure-entra"):
            return "privilege" if has_elevated_permissions else "access"
        # Developer tools default to 'access'
        return "access"

    @staticmethod
    def map_to_platform(provider: str) -> str:
        """
        Map provider to platform enum value.

        Args:
            provider: Provider name

        Returns:
            Platform value string
        """
        mapping = {
            "openai": "openai",
            "anthropic": "anthropic",
            "aws": "aws",
            "google-cloud": "google-a2a",
            "azure-entra": "azure-ai",
            "github": "github",
            "twilio": "custom",
            "sendgrid": "custom",
        }
        return mapping.get(provider, "custom")
