"""AuthProvider implementations and dependency-injection factory.

Public surface:

- :class:`AuthProvider` (re-exported from :mod:`.base`) — the protocol
  every backend must satisfy.
- :class:`LocalAuthProvider` — SQLite + bcrypt default.
- :class:`StubOIDCProvider` — interface placeholder for the future
  OIDC integration.
- :class:`User` — provider-agnostic user value object.
- :func:`get_auth_provider` — DI factory used by routes via
  ``Depends(get_auth_provider)``.
- :func:`reset_auth_provider` — clears the lru_cache singleton
  (used in tests + when the auth provider config changes at runtime).

Configuration
-------------
The provider is selected via the ``AGENTKIT_AUTH_PROVIDER`` environment
variable (default: ``"local"``). When the future ``auth.provider``
field is added to ``agentkit.yaml`` it should override the env var.

Adding a new provider
---------------------
1. Create ``auth/providers/<name>.py`` with a class that satisfies
   the :class:`AuthProvider` Protocol (i.e. has ``name: str`` and the
   four async methods).
2. Import it here and add a branch to :func:`get_auth_provider`.
3. Add it to ``AGENTKIT_AUTH_PROVIDER`` enum in the deployment docs.
"""

from __future__ import annotations

import logging
import os
from functools import lru_cache
from pathlib import Path

from .base import AuthProvider
from .exceptions import AuthProviderError, InvalidCredentials, ProviderNotImplemented
from .local import LocalAuthProvider
from .oidc_stub import StubOIDCProvider
from .user import User

logger = logging.getLogger(__name__)


# Re-exports for ergonomic imports: `from agentkit.server.auth.providers import AuthProvider`
__all__ = [
    "AuthProvider",
    "AuthProviderError",
    "InvalidCredentials",
    "LocalAuthProvider",
    "ProviderNotImplemented",
    "StubOIDCProvider",
    "User",
    "get_auth_provider",
    "reset_auth_provider",
]


# Default provider name. Configurable via AGENTKIT_AUTH_PROVIDER env var
# (overridable when the auth.provider field is added to agentkit.yaml).
DEFAULT_AUTH_PROVIDER = "local"


def _resolve_provider_name() -> str:
    """Return the configured provider name, defaulting to 'local'."""
    name = os.environ.get("AGENTKIT_AUTH_PROVIDER", DEFAULT_AUTH_PROVIDER).strip()
    if not name:
        return DEFAULT_AUTH_PROVIDER
    return name


def _resolve_db_path() -> Path:
    """Return the auth DB path (overridable for tests via env var)."""
    from ..models import DEFAULT_AUTH_DB_PATH

    env = os.environ.get("AGENTKIT_AUTH_DB")
    if env:
        return Path(env)
    return DEFAULT_AUTH_DB_PATH


@lru_cache(maxsize=1)
def get_auth_provider() -> AuthProvider:
    """Return the configured :class:`AuthProvider` (memoized singleton).

    The ``lru_cache`` is process-local and the value is resolved on
    first call. Use :func:`reset_auth_provider` to clear the cache
    (e.g. when the configuration changes at runtime, or in test
    fixtures that need a fresh provider with a different db_path).

    Raises:
        ValueError: if the configured provider name is not recognized.
    """
    name = _resolve_provider_name()
    if name == "local":
        return LocalAuthProvider(db_path=_resolve_db_path())
    if name == "oidc-stub":
        return StubOIDCProvider()
    raise ValueError(
        f"unknown auth provider: {name!r}. "
        f"Supported providers: 'local', 'oidc-stub'. "
        f"Set AGENTKIT_AUTH_PROVIDER or update agentkit.yaml's auth.provider field."
    )


def reset_auth_provider() -> None:
    """Clear the memoized :class:`AuthProvider` singleton.

    Use in tests (e.g. in a fixture's teardown) or in code paths that
    change the auth provider configuration at runtime and need a
    re-resolution.
    """
    get_auth_provider.cache_clear()
