"""
ID Wispera CrewAI Integration
Secret manager for CrewAI agents
"""

from typing import Optional, Any
from pathlib import Path


class WisperaSecretManager:
    """
    Secret manager for CrewAI that retrieves credentials from ID Wispera vault.

    Usage:
        from id_wispera.integrations.crewai import WisperaSecretManager
        from crewai import Agent

        secret_manager = WisperaSecretManager()
        agent = Agent(
            role="researcher",
            llm_config=secret_manager.get_llm_config("anthropic-research")
        )
    """

    def __init__(
        self,
        vault_path: Optional[str] = None,
        passphrase: Optional[str] = None,
        passphrase_env: str = "IDW_PASSPHRASE",
    ):
        """
        Initialize the secret manager.

        Args:
            vault_path: Path to the vault directory. Defaults to ~/.id-wispera
            passphrase: **Deprecated.** Do not pass a plaintext passphrase.
                Use ``idw auth login`` (OS keychain) or ``idw auth token create``
                (session token) instead.  This parameter will be removed in a
                future release.
            passphrase_env: Ignored when using PassphraseProvider. Retained
                for backward compatibility.
        """
        import os
        import warnings
        from id_wispera.auth import PassphraseProvider

        if vault_path is None:
            vault_path = os.path.expanduser("~/.id-wispera")

        self._vault_path = Path(vault_path)
        self._vault: Optional["Vault"] = None

        if passphrase is not None:
            warnings.warn(
                "Passing 'passphrase' directly is deprecated and will be removed "
                "in a future release. Use 'idw auth login' or session tokens instead.",
                DeprecationWarning,
                stacklevel=2,
            )
            self._passphrase: Optional[str] = passphrase
        else:
            provider = PassphraseProvider(
                allow_interactive=False,
                allow_env_var=True,
            )
            self._passphrase = provider.get_passphrase()

    def _ensure_vault(self) -> "Vault":
        """Ensure vault is loaded and unlocked."""
        if self._vault is None:
            from id_wispera.vault import Vault

            vault_file = self._vault_path / "vault.json"
            if not vault_file.exists():
                raise FileNotFoundError(
                    f"Vault not found at {vault_file}. Run 'idw-py init' first."
                )

            self._vault = Vault.load(vault_file, self._passphrase)

        return self._vault

    def _get_passport(self, passport_name: str, platform: Optional[str] = None):
        """Get a passport by name with optional platform filter."""
        from id_wispera.types import Passport

        vault = self._ensure_vault()
        passports = vault.list_passports()

        for passport in passports:
            if passport.name == passport_name:
                if platform and platform not in passport.platforms:
                    continue

                if passport.status not in ("active", "expiring"):
                    raise ValueError(
                        f"Passport '{passport_name}' is {passport.status}"
                    )

                return passport

        raise ValueError(f"Passport not found: {passport_name}")

    def get(self, passport_name: str, platform: Optional[str] = None) -> str:
        """
        Get a credential value by passport name.

        Args:
            passport_name: Name of the passport
            platform: Optional platform filter

        Returns:
            The credential value
        """
        from id_wispera.audit import log_action
        from id_wispera.types import AuditAction

        passport = self._get_passport(passport_name, platform)
        vault = self._ensure_vault()

        log_action(
            vault,
            passport_id=passport.id,
            action=AuditAction.ACCESSED,
            actor="crewai",
            platform=platform or "crewai",
            details="Credential accessed via CrewAI secret manager",
        )

        return passport.credential_value

    def get_llm_config(
        self,
        passport_name: str,
        model: Optional[str] = None,
        **extra_config: Any,
    ) -> dict[str, Any]:
        """
        Get LLM configuration for CrewAI.

        Args:
            passport_name: Name of the passport containing the API key
            model: Optional model name to use
            **extra_config: Additional configuration options

        Returns:
            LLM configuration dictionary
        """
        passport = self._get_passport(passport_name)

        # Determine provider from platforms
        provider = self._detect_provider(passport.platforms)

        config: dict[str, Any] = {
            "api_key": passport.credential_value,
        }

        if model:
            config["model"] = model
        elif provider == "openai":
            config["model"] = "gpt-4"
        elif provider == "anthropic":
            config["model"] = "claude-3-5-sonnet-20241022"
        elif provider == "azure":
            config["model"] = "gpt-4"

        # Add provider-specific config
        if provider == "openai":
            config["provider"] = "openai"
        elif provider == "anthropic":
            config["provider"] = "anthropic"
        elif provider == "azure":
            config["provider"] = "azure"

        # Merge extra config
        config.update(extra_config)

        # Log access
        from id_wispera.audit import log_action
        from id_wispera.types import AuditAction

        vault = self._ensure_vault()
        log_action(
            vault,
            passport_id=passport.id,
            action=AuditAction.ACCESSED,
            actor="crewai",
            platform=provider or "crewai",
            details=f"LLM config generated for model: {config.get('model', 'default')}",
        )

        return config

    def _detect_provider(self, platforms: list[str]) -> Optional[str]:
        """Detect the provider from platform list."""
        provider_map = {
            "openai": "openai",
            "anthropic": "anthropic",
            "azure-ai": "azure",
            "google-a2a": "google",
        }

        for platform in platforms:
            if platform in provider_map:
                return provider_map[platform]

        return None

    def get_tool_credentials(
        self,
        tool_name: str,
        passport_name: Optional[str] = None,
    ) -> dict[str, str]:
        """
        Get credentials for a CrewAI tool.

        Args:
            tool_name: Name of the tool (e.g., "serper", "browserbase")
            passport_name: Optional passport name, defaults to tool_name

        Returns:
            Dictionary of credentials for the tool
        """
        if passport_name is None:
            passport_name = tool_name

        passport = self._get_passport(passport_name)

        # Tool-specific credential mapping
        tool_credentials = {
            "serper": {"SERPER_API_KEY": passport.credential_value},
            "browserbase": {"BROWSERBASE_API_KEY": passport.credential_value},
            "exa": {"EXA_API_KEY": passport.credential_value},
            "firecrawl": {"FIRECRAWL_API_KEY": passport.credential_value},
        }

        if tool_name in tool_credentials:
            return tool_credentials[tool_name]

        # Default: return as generic API_KEY
        return {"API_KEY": passport.credential_value}

    def list_available(self, platform: Optional[str] = None) -> list[str]:
        """
        List available passport names.

        Args:
            platform: Optional platform filter

        Returns:
            List of passport names
        """
        vault = self._ensure_vault()
        passports = vault.list_passports()

        names = []
        for passport in passports:
            if passport.status in ("active", "expiring"):
                if platform is None or platform in passport.platforms:
                    names.append(passport.name)

        return names


class WisperaToolCredentials:
    """
    Context manager for accessing tool credentials without environment injection.

    Credentials are decrypted from the vault and made available via the
    ``get()`` method.  They are **never** written to ``os.environ``.

    Usage:
        secret_manager = WisperaSecretManager()
        with WisperaToolCredentials(secret_manager, "serper") as creds:
            api_key = creds.get("SERPER_API_KEY")
            tool = SerperDevTool(api_key=api_key)
    """

    def __init__(
        self,
        secret_manager: WisperaSecretManager,
        tool_name: str,
        passport_name: Optional[str] = None,
    ):
        self._secret_manager = secret_manager
        self._tool_name = tool_name
        self._passport_name = passport_name
        self._credentials: dict[str, str] = {}

    def __enter__(self) -> "WisperaToolCredentials":
        self._credentials = self._secret_manager.get_tool_credentials(
            self._tool_name,
            self._passport_name,
        )
        return self

    def get(self, key: str) -> str:
        """
        Get a credential value by its key name.

        Args:
            key: The credential key (e.g. ``SERPER_API_KEY``).

        Returns:
            The decrypted credential value.

        Raises:
            KeyError: If the key is not found in the tool's credentials.
        """
        if key not in self._credentials:
            raise KeyError(
                f"Credential key '{key}' not found for tool '{self._tool_name}'. "
                f"Available keys: {list(self._credentials.keys())}"
            )
        return self._credentials[key]

    def keys(self) -> list[str]:
        """Return available credential key names."""
        return list(self._credentials.keys())

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        # Clear in-memory credentials on context exit
        self._credentials.clear()
