import os
import logging
import shutil
from typing import Callable

from ._types import ProviderResolution, NoProviderAvailable
from .nexus import is_nexus_running
from .managed_keys import get_managed_key
from .auth import get_auth_context

logger = logging.getLogger("iis_access")

# Known env var names for local API keys
_ENV_KEY_MAP: dict[str, str] = {
    "openai": "OPENAI_API_KEY",
    "anthropic": "ANTHROPIC_API_KEY",
    "elevenlabs": "ELEVENLABS_API_KEY",
    "azure-openai": "AZURE_OPENAI_API_KEY",
    "google-ai": "GOOGLE_AI_API_KEY",
    "mistral": "MISTRAL_API_KEY",
    "groq": "GROQ_API_KEY",
    "deepseek": "DEEPSEEK_API_KEY",
}

# Free provider registry
_FREE_PROVIDERS: dict[str, list[tuple[str, Callable[[], bool] | None]]] = {
    "tts": [
        ("edge-tts", None),  # Always available (pip package)
        ("mac-say", lambda: shutil.which("say") is not None),
    ],
    "transcription": [
        ("whisper-local", lambda: shutil.which("whisper") is not None),
    ],
}


def register_free_provider(
    task_type: str,
    provider: str,
    check: Callable[[], bool] | None = None,
) -> None:
    """Register a free provider for a task type."""
    if task_type not in _FREE_PROVIDERS:
        _FREE_PROVIDERS[task_type] = []
    _FREE_PROVIDERS[task_type].append((provider, check))


async def resolve_provider(
    task_type: str,
    preferred: str | None = None,
    require_capability: str | None = None,
) -> ProviderResolution:
    """
    Resolve the best provider for the given task type.

    Resolution order:
    1. Nexus with pooled credits
    2. Nexus with managed key
    3. Local API key (env var)
    4. Free provider
    5. Raise NoProviderAvailable
    """
    context = await get_auth_context()

    # Branch 1 & 2: Nexus
    if await is_nexus_running():
        nexus_url = _get_nexus_endpoint()

        # Branch 1: Nexus with pooled credits (if authenticated with plan)
        if context and context.plan in ("pro", "enterprise"):
            return ProviderResolution(
                provider=preferred or "nexus-default",
                method="nexus",
                api_key=None,
                endpoint=nexus_url,
                credits_estimated=None,  # Nexus handles pricing
            )

        # Branch 2: Nexus with managed key
        if context and preferred:
            managed_key = await get_managed_key(preferred)
            if managed_key:
                return ProviderResolution(
                    provider=preferred,
                    method="managed_key",
                    api_key=managed_key,
                    endpoint=nexus_url,
                    credits_estimated=None,
                )

    # Branch 3: Local API key
    providers_to_check = [preferred] if preferred else list(_ENV_KEY_MAP.keys())
    for provider in providers_to_check:
        if not provider:
            continue
        env_var = _ENV_KEY_MAP.get(provider)
        if env_var:
            key = os.environ.get(env_var)
            if key:
                return ProviderResolution(
                    provider=provider,
                    method="local_key",
                    api_key=key,
                    endpoint=None,
                    credits_estimated=None,
                )

    # Also check managed key without Nexus (direct access)
    if context and preferred:
        managed_key = await get_managed_key(preferred)
        if managed_key:
            return ProviderResolution(
                provider=preferred,
                method="managed_key",
                api_key=managed_key,
                endpoint=None,
                credits_estimated=None,
            )

    # Branch 4: Free provider
    free_providers = _FREE_PROVIDERS.get(task_type, [])
    for name, check_fn in free_providers:
        if check_fn is None or check_fn():
            return ProviderResolution(
                provider=name,
                method="free",
                api_key=None,
                endpoint=None,
                credits_estimated=0,
            )

    # Branch 5: No provider available
    raise NoProviderAvailable(
        f"No provider available for task type '{task_type}'"
        + (f" (preferred: {preferred})" if preferred else "")
    )


def _get_nexus_endpoint() -> str:
    from .config import get_nexus_url

    return get_nexus_url()
