"""
ID Wispera Provisioning Module
Unified interface for provisioning credentials through vendor APIs
and wrapping them as governed passports in a single workflow.
"""

from typing import Any, Optional

from id_wispera.types import Passport, CreatePassportInput
from id_wispera.vault import Vault
from id_wispera.passport import create_passport
from id_wispera.provisioning.types import (
    ProvisioningProvider,
    ProviderCapabilities,
    ProviderAuth,
    ProvisioningRequest,
    ProvisioningResult,
    RotationResult,
    OpenAIConfig,
    AWSConfig,
    GoogleCloudConfig,
    AzureEntraConfig,
    GitHubConfig,
    TwilioConfig,
    SendGridConfig,
    AnthropicConfig,
)
from id_wispera.provisioning.provider import ProvisioningProviderBase
from id_wispera.provisioning.providers.openai import OpenAIProvider
from id_wispera.provisioning.providers.aws import AWSProvider
from id_wispera.provisioning.admin_passport import (
    create_admin_passport,
    find_admin_passport,
    get_provider_auth,
    list_admin_passports,
    delete_admin_passport,
)


# ============================================================================
# Provider Registry
# ============================================================================

_provider_registry: dict[str, ProvisioningProviderBase] = {
    "openai": OpenAIProvider(),
    "aws": AWSProvider(),
}


# ============================================================================
# Unified Interface
# ============================================================================

def provision_and_create_passport(
    vault: Vault,
    request: ProvisioningRequest,
    auth: ProviderAuth,
) -> tuple[ProvisioningResult, Optional[Passport]]:
    """
    Provision a new credential AND create a governed passport in one operation.
    The credential is provisioned via the vendor API, then stored in the vault
    as a fully governed passport with delegation chain and audit trail.

    Args:
        vault: The unlocked vault
        request: Provisioning request with provider, name, owner, etc.
        auth: Provider authentication configuration

    Returns:
        Tuple of (ProvisioningResult, Passport or None)

    Raises:
        ValueError: If the provider is unknown
    """
    provider_name = request.provider
    if isinstance(provider_name, ProvisioningProvider):
        provider_name = provider_name.value

    provider = _provider_registry.get(provider_name)
    if not provider:
        raise ValueError(f"Unknown provider: {provider_name}")

    # Initialize and provision
    provider.initialize(auth)
    result = provider.provision(request)

    if not result.success or not result.passport_input:
        return result, None

    # Create the passport using the pre-populated input
    passport_data = result.passport_input
    passport_input = CreatePassportInput(**passport_data)
    passport = create_passport(vault, passport_input)

    return result, passport


def provision(
    request: ProvisioningRequest,
    auth: ProviderAuth,
) -> ProvisioningResult:
    """
    Provision only (no passport creation) - for advanced use cases
    where you want to handle passport creation separately.

    Args:
        request: Provisioning request
        auth: Provider authentication configuration

    Returns:
        ProvisioningResult with the outcome

    Raises:
        ValueError: If the provider is unknown
    """
    provider_name = request.provider
    if isinstance(provider_name, ProvisioningProvider):
        provider_name = provider_name.value

    provider = _provider_registry.get(provider_name)
    if not provider:
        raise ValueError(f"Unknown provider: {provider_name}")

    provider.initialize(auth)
    return provider.provision(request)


def rotate_credential(
    provider: str,
    existing_key_id: str,
    request: ProvisioningRequest,
    auth: ProviderAuth,
) -> Optional[dict]:
    """
    Rotate a credential - creates a new key, returns the old key ID for revocation.
    Use revoke_at_source() after a propagation delay to complete the rotation.

    Args:
        provider: Provider name
        existing_key_id: ID of the key to rotate
        request: Provisioning request for the new key
        auth: Provider authentication configuration

    Returns:
        Dict with newCredential, oldKeyId, recommendedRevocationDelay, or None
    """
    provider_val = provider.value if isinstance(provider, ProvisioningProvider) else provider
    p = _provider_registry.get(provider_val)
    if not p or not p.capabilities.can_rotate:
        return None

    p.initialize(auth)
    try:
        result = p.rotate(existing_key_id, request)
        return result.model_dump(by_alias=True)
    except NotImplementedError:
        return None


def revoke_at_source(
    provider: str,
    key_id: str,
    auth: ProviderAuth,
) -> dict:
    """
    Revoke a credential at the source vendor.

    Args:
        provider: Provider name
        key_id: The key ID to revoke
        auth: Provider authentication configuration

    Returns:
        Dict with 'success' and optional 'error' keys
    """
    provider_val = provider.value if isinstance(provider, ProvisioningProvider) else provider
    p = _provider_registry.get(provider_val)
    if not p or not p.capabilities.can_revoke:
        return {"success": False, "error": "Provider does not support revocation"}

    p.initialize(auth)
    try:
        return p.revoke(key_id)
    except NotImplementedError:
        return {"success": False, "error": "Provider does not support revocation"}


def list_provider_keys(
    provider: str,
    auth: ProviderAuth,
) -> list[dict]:
    """
    List existing keys at the source vendor.

    Args:
        provider: Provider name
        auth: Provider authentication configuration

    Returns:
        List of dicts with id, name, createdAt, status

    Raises:
        ValueError: If provider doesn't support listing
    """
    provider_val = provider.value if isinstance(provider, ProvisioningProvider) else provider
    p = _provider_registry.get(provider_val)
    if not p or not p.capabilities.can_list:
        raise ValueError(f"Provider {provider_val} does not support listing keys")

    p.initialize(auth)
    return p.list_keys()


def validate_provider_auth(
    provider: str,
    auth: ProviderAuth,
) -> dict:
    """
    Validate authentication credentials for a provider.

    Args:
        provider: Provider name
        auth: Provider authentication configuration

    Returns:
        Dict with 'valid' and optional 'error' keys
    """
    provider_val = provider.value if isinstance(provider, ProvisioningProvider) else provider
    p = _provider_registry.get(provider_val)
    if not p:
        return {"valid": False, "error": f"Provider {provider_val} not found"}

    p.initialize(auth)
    try:
        return p.validate_auth()
    except NotImplementedError:
        return {"valid": False, "error": f"Provider {provider_val} does not support auth validation"}


def get_provider_capabilities(provider: str) -> Optional[ProviderCapabilities]:
    """
    Get capabilities for a provider.

    Args:
        provider: Provider name

    Returns:
        ProviderCapabilities or None if provider not found
    """
    provider_val = provider.value if isinstance(provider, ProvisioningProvider) else provider
    p = _provider_registry.get(provider_val)
    return p.capabilities if p else None


def list_providers() -> list[dict]:
    """
    List all available providers and their capabilities.

    Returns:
        List of dicts with 'provider' and 'capabilities' keys
    """
    return [
        {
            "provider": name,
            "capabilities": impl.capabilities.model_dump(by_alias=True),
        }
        for name, impl in _provider_registry.items()
    ]


__all__ = [
    # Functions
    "provision_and_create_passport",
    "provision",
    "rotate_credential",
    "revoke_at_source",
    "list_provider_keys",
    "validate_provider_auth",
    "get_provider_capabilities",
    "list_providers",
    # Types
    "ProvisioningProvider",
    "ProviderCapabilities",
    "ProviderAuth",
    "ProvisioningRequest",
    "ProvisioningResult",
    "RotationResult",
    "OpenAIConfig",
    "AWSConfig",
    "GoogleCloudConfig",
    "AzureEntraConfig",
    "GitHubConfig",
    "TwilioConfig",
    "SendGridConfig",
    "AnthropicConfig",
    # Provider base
    "ProvisioningProviderBase",
    "OpenAIProvider",
    "AWSProvider",
    # Admin passport management
    "create_admin_passport",
    "find_admin_passport",
    "get_provider_auth",
    "list_admin_passports",
    "delete_admin_passport",
]
