"""Pluggable secrets provider for runtime secret resolution.

Supports three backends:
- ``env`` (default): Reads secrets from environment variables.
- ``aws``: Reads from AWS Secrets Manager (requires ``boto3``).
- ``vault``: Reads from HashiCorp Vault (requires ``hvac``).

Configure via environment variables:
    SECRETS_BACKEND=env|aws|vault
    # AWS-specific
    AWS_SECRETS_REGION=us-east-1
    AWS_SECRET_NAME=meta-ads-mcp/prod
    # Vault-specific
    VAULT_ADDR=https://vault.example.com
    VAULT_TOKEN=...
    VAULT_SECRET_PATH=secret/data/meta-ads-mcp
"""
from __future__ import annotations

import json
import os
from abc import ABC, abstractmethod
from typing import Dict, Optional

from .logging import get_logger

logger = get_logger(__name__)


class SecretsProvider(ABC):
    """Abstract interface for secret resolution."""

    @abstractmethod
    async def get_secret(self, key: str) -> Optional[str]:
        """Return the value for *key*, or ``None`` if not found."""

    async def get_secrets(self, keys: list[str]) -> Dict[str, Optional[str]]:
        """Bulk-fetch multiple secrets."""
        return {k: await self.get_secret(k) for k in keys}

    async def close(self) -> None:
        """Release any resources held by the provider."""


class EnvSecretsProvider(SecretsProvider):
    """Resolve secrets from environment variables (default)."""

    async def get_secret(self, key: str) -> Optional[str]:
        return os.getenv(key)


class AWSSecretsProvider(SecretsProvider):
    """Resolve secrets from AWS Secrets Manager."""

    def __init__(
        self,
        secret_name: str,
        region: str = "us-east-1",
    ) -> None:
        try:
            import boto3
        except ImportError as exc:
            raise ImportError(
                "boto3 is required for AWS secrets backend: pip install boto3"
            ) from exc
        self._client = boto3.client("secretsmanager", region_name=region)
        self._secret_name = secret_name
        self._cache: Dict[str, str] | None = None

    async def _load(self) -> Dict[str, str]:
        if self._cache is not None:
            return self._cache
        response = self._client.get_secret_value(SecretId=self._secret_name)
        self._cache = json.loads(response["SecretString"])
        logger.info("aws_secrets_loaded secret=%s", self._secret_name)
        return self._cache

    async def get_secret(self, key: str) -> Optional[str]:
        secrets = await self._load()
        return secrets.get(key)

    def invalidate_cache(self) -> None:
        """Force re-fetch on next access (supports rotation)."""
        self._cache = None


class VaultSecretsProvider(SecretsProvider):
    """Resolve secrets from HashiCorp Vault KV v2."""

    def __init__(
        self,
        addr: str,
        token: str,
        secret_path: str,
    ) -> None:
        try:
            import hvac
        except ImportError as exc:
            raise ImportError(
                "hvac is required for Vault secrets backend: pip install hvac"
            ) from exc
        self._client = hvac.Client(url=addr, token=token)
        self._secret_path = secret_path
        self._cache: Dict[str, str] | None = None

    async def _load(self) -> Dict[str, str]:
        if self._cache is not None:
            return self._cache
        mount, _, path = self._secret_path.partition("/")
        response = self._client.secrets.kv.v2.read_secret_version(
            path=path, mount_point=mount or "secret"
        )
        self._cache = response["data"]["data"]
        logger.info("vault_secrets_loaded path=%s", self._secret_path)
        return self._cache

    async def get_secret(self, key: str) -> Optional[str]:
        secrets = await self._load()
        return secrets.get(key)

    def invalidate_cache(self) -> None:
        self._cache = None

    async def close(self) -> None:
        if hasattr(self._client, "close"):
            self._client.close()


# ---------------------------------------------------------------------------
# Factory
# ---------------------------------------------------------------------------

_provider: SecretsProvider | None = None


def get_secrets_provider() -> SecretsProvider:
    """Return the configured secrets provider (lazy singleton)."""
    global _provider
    if _provider is not None:
        return _provider

    backend = os.getenv("SECRETS_BACKEND", "env").lower()

    if backend == "aws":
        _provider = AWSSecretsProvider(
            secret_name=os.getenv("AWS_SECRET_NAME", "meta-ads-mcp/prod"),
            region=os.getenv("AWS_SECRETS_REGION", "us-east-1"),
        )
    elif backend == "vault":
        _provider = VaultSecretsProvider(
            addr=os.environ["VAULT_ADDR"],
            token=os.environ["VAULT_TOKEN"],
            secret_path=os.getenv("VAULT_SECRET_PATH", "secret/data/meta-ads-mcp"),
        )
    else:
        _provider = EnvSecretsProvider()

    logger.info("secrets_provider_initialized backend=%s", backend)
    return _provider


async def close_secrets_provider() -> None:
    global _provider
    if _provider is not None:
        await _provider.close()
        _provider = None
