"""Shared authentication helpers for CLI commands."""

from __future__ import annotations

from collections.abc import Iterator
from contextlib import contextmanager

try:
    from shared import AuthenticationError, BackendClient
except ImportError:
    from cli._vendor.shared import AuthenticationError, BackendClient

from ..config import CLIConfig
from ..context import CLIContext
from ..exceptions import CLIError, ConfigurationError, KeyringFailure
from ..keychain import get_api_key_with_env_override

NOT_SIGNED_IN_MESSAGE = "Not signed in. Run 'ctxme auth login' to authenticate."


def require_auth(config: CLIConfig) -> None:
    """Check authentication before other operations.

    Call this before _resolve_project() to ensure auth errors surface before
    project configuration errors.

    Args:
        config: CLI configuration (used to check mock mode).

    Raises:
        CLIError: If not authenticated (no API key or keyring failure).
    """
    # Mock mode doesn't require authentication
    if config.use_mock_client:
        return

    try:
        api_key, _ = get_api_key_with_env_override()
    except KeyringFailure as exc:
        raise CLIError(NOT_SIGNED_IN_MESSAGE) from exc

    if not api_key:
        raise CLIError(NOT_SIGNED_IN_MESSAGE)


@contextmanager
def authenticated_client(
    cli_ctx: CLIContext,
    config: CLIConfig,
) -> Iterator[BackendClient]:
    """Yield a backend client and map auth failures to user-friendly errors."""
    try:
        with cli_ctx.client(config) as client:
            yield client
    except ConfigurationError as exc:
        if "API key is not set." in str(exc):
            raise CLIError(NOT_SIGNED_IN_MESSAGE) from exc
        raise
    except AuthenticationError as exc:
        raise CLIError(NOT_SIGNED_IN_MESSAGE) from exc
    except KeyringFailure as exc:
        raise CLIError(NOT_SIGNED_IN_MESSAGE) from exc
