"""
Azure Key Vault secret management module.

Provides a module-level interface to Azure Key Vault for secret management.

Usage:
    Inside Fabric (authentication is automatic):

        >>> from fabias.integrations import keyvault
        >>> keyvault.client(vault_url="https://my-vault.vault.azure.net/")
        >>> password = keyvault.get("database-password")

    Standalone (provide credentials):

        >>> from fabias.integrations import keyvault
        >>> from fabias import ServicePrincipalAuth
        >>> auth = ServicePrincipalAuth(
        ...     tenant_id="...",
        ...     client_id="...",
        ...     client_secret="..."
        ... )
        >>> keyvault.client(vault_url="https://my-vault.vault.azure.net/", auth=auth)
        >>> password = keyvault.get("database-password")
        >>> keyvault.set("new-key", "new-value")
"""

from typing import Any, Dict, List, Optional, cast

from ..._shared.auth import AuthProvider, AutoAuth, FabricAuth, ServicePrincipalAuth
from ..._shared.client import BaseClient
from ..._shared.exceptions import AuthenticationError, FabiasError
from ..._shared.runtime import runtime


class KeyVaultClient(BaseClient):
    """
    HTTP client for Azure Key Vault REST API.

    Provides authenticated access to Key Vault secrets.
    """

    KEYVAULT_SCOPE = "https://vault.azure.net/.default"

    def __init__(
        self,
        vault_url: str,
        auth: Optional[AuthProvider] = None,
        tenant_id: Optional[str] = None,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None,
    ):
        """
        Initialize the Key Vault client.

        Args:
            vault_url: Full Key Vault URL (e.g., 'https://myvault.vault.azure.net/')
            auth: Optional pre-configured AuthProvider
            tenant_id: Azure AD tenant ID (for service principal auth)
            client_id: Application client ID (for service principal auth)
            client_secret: Client secret (for service principal auth)
        """
        # Determine authentication method
        if auth:
            auth_provider = auth
        elif tenant_id and client_id and client_secret:
            auth_provider = ServicePrincipalAuth(
                tenant_id=tenant_id, client_id=client_id, client_secret=client_secret
            )
        elif runtime.isFabric:
            auth_provider = FabricAuth()
        else:
            auth_provider = AutoAuth(
                tenant_id=tenant_id, client_id=client_id, client_secret=client_secret
            )

        super().__init__(auth_provider)

        # Normalize vault URL
        if not vault_url.endswith("/"):
            vault_url = vault_url + "/"

        self._vault_url = vault_url
        self._base_uri = vault_url
        self._scope = self.KEYVAULT_SCOPE

    @property
    def vaultUrl(self) -> str:
        """Get the configured vault URL."""
        return self._vault_url

    def getSecret(self, key: str) -> str:
        """
        Retrieve a secret from Key Vault.

        In Fabric environments, attempts to use notebookutils.credentials first
        for better performance and simpler auth. Falls back to REST API.

        Args:
            key: Secret name

        Returns:
            str: Secret value

        Raises:
            FabiasError: If secret not found or retrieval fails
        """
        # In Fabric, try notebookutils first (faster, simpler auth)
        if runtime.isFabric:
            notebookutils_module = runtime.notebookutils
            if notebookutils_module and hasattr(notebookutils_module, "credentials"):
                try:
                    return cast(
                        str, notebookutils_module.credentials.getSecret(self._vault_url, key)
                    )
                except Exception:
                    # Fall through to REST API
                    pass

        # Use REST API
        url = f"secrets/{key}?api-version=7.3"
        response = self.get(url)
        data = response.json()
        value = data.get("value")
        if value is None:
            raise FabiasError(f"Secret '{key}' not found or has no value")
        return cast(str, value)

    def setSecret(self, key: str, value: str) -> None:
        """
        Set a secret in Key Vault.

        Args:
            key: Secret name
            value: Secret value

        Raises:
            FabiasError: If write fails
        """
        url = f"secrets/{key}?api-version=7.3"
        body = {"value": value}

        response = self.put(url, data=body)
        data = response.json()

        if data.get("value") == value:
            print(f'Key Vault secret "{key}" modified.')

    def listSecrets(self) -> List[Dict[str, Any]]:
        """
        List all secrets in the Key Vault.

        Returns:
            List[Dict[str, Any]]: List of secret metadata (id, attributes, etc.)
        """
        url = "secrets?api-version=7.3"
        response = self.get(url)
        data = response.json()
        return cast(List[Dict[str, Any]], data.get("value", []))


# =============================================================================
# Module-level singleton pattern
# =============================================================================

_client: Optional[KeyVaultClient] = None
_configured: bool = False


def client(
    vault_url: str,
    tenant_id: Optional[str] = None,
    client_id: Optional[str] = None,
    client_secret: Optional[str] = None,
    auth: Optional[AuthProvider] = None,
) -> KeyVaultClient:
    """
    Create or configure the Key Vault client.

    When called as a module-level function, sets up the singleton for convenience.
    Returns the client instance for both module-level and explicit multi-client use.

    Args:
        vault_url: Full Key Vault URL (e.g., 'https://myvault.vault.azure.net/')
        tenant_id: Azure AD tenant ID
        client_id: Application (client) ID
        client_secret: Client secret value
        auth: Pre-configured AuthProvider (recommended)

    Returns:
        KeyVaultClient: Configured client instance

    Examples:
        Module-level singleton (most common):

        >>> import fabias.secrets as secrets
        >>> secrets.client(
        ...     vault_url="https://my-vault.vault.azure.net/",
        ...     tenant_id="your-tenant-id",
        ...     client_id="your-client-id",
        ...     client_secret="your-secret"
        ... )
        >>> value = secrets.get("my-secret")

        With pre-configured auth:

        >>> from fabias._shared.auth import ServicePrincipalAuth
        >>> auth = ServicePrincipalAuth(tenant_id, client_id, secret)
        >>> secrets.client(vault_url="https://my-vault.vault.azure.net/", auth=auth)
        >>> value = secrets.get("my-secret")

        Multi-client usage:

        >>> client1 = secrets.client(vault_url=vault1_url, auth=auth1)
        >>> client2 = secrets.client(vault_url=vault2_url, auth=auth2)
        >>> secret1 = client1.get("db-password")
        >>> secret2 = client2.get("api-key")
    """
    global _client, _configured

    new_client = KeyVaultClient(
        vault_url=vault_url,
        auth=auth,
        tenant_id=tenant_id,
        client_id=client_id,
        client_secret=client_secret,
    )

    # Set as module-level singleton
    _client = new_client
    _configured = True

    return new_client


def _get_client() -> KeyVaultClient:
    """
    Get the configured KeyVaultClient.

    Raises:
        FabiasError: If secrets.client() has not been called
    """
    if _client is None:
        raise FabiasError(
            "Secrets module not configured. Call secrets.client(vault_url=...) first."
        )
    return _client


def reset() -> None:
    """Reset the module state, clearing the cached client."""
    global _client, _configured
    _client = None
    _configured = False


def vault() -> Optional[str]:
    """Get the configured vault URL, or None if not configured."""
    return _client.vaultUrl if _client else None


# =============================================================================
# Module-level API functions
# =============================================================================


def get(key: str) -> str:
    """
    Get a secret from Azure Key Vault.

    Requires secrets.client() to be called first.

    Args:
        key: Secret name/key in Key Vault

    Returns:
        str: Secret value

    Raises:
        FabiasError: If module not configured or secret not found

    Examples:
        >>> from fabias import secrets
        >>> secrets.client(vault_url="https://my-vault.vault.azure.net/")
        >>> password = secrets.get('database-password')
    """
    return _get_client().getSecret(key)


def set(key: str, value: str) -> None:
    """
    Set a secret in Azure Key Vault.

    Requires secrets.client() to be called first.

    Args:
        key: Secret name/key in Key Vault
        value: Secret value to store

    Raises:
        FabiasError: If module not configured or operation fails

    Examples:
        >>> from fabias import secrets
        >>> secrets.client(vault_url="https://my-vault.vault.azure.net/")
        >>> secrets.set('api-key', 'new-api-key-value')
    """
    _get_client().setSecret(key, value)


def list() -> List[Dict[str, Any]]:
    """
    List all secrets in the Key Vault.

    Returns:
        List[Dict[str, Any]]: List of secret metadata (id, attributes, etc.)

    Examples:
        >>> from fabias import secrets
        >>> for s in secrets.list():
        ...     print(s['id'])
    """
    return _get_client().listSecrets()


__all__ = [
    # Configuration
    "client",
    "reset",
    "vault",
    # Operations
    "get",
    "set",
    "list",
    # Classes (for advanced usage)
    "KeyVaultClient",
]
