"""Command infrastructure for consistent error handling and setup."""

import asyncio
from collections.abc import AsyncGenerator, Coroutine
from contextlib import asynccontextmanager
from functools import wraps
from typing import Any, Callable, ParamSpec

import typer

from cli.infrastructure.client_factory import create_client
from cli.infrastructure.config import CLIConfig, load_config
from cli.infrastructure.error_display import display_error
from alloy_runtime_sdk.api_client.client import ApiClient

P = ParamSpec("P")


def async_command(func: Callable[P, Coroutine[Any, Any, None]]) -> Callable[P, None]:
    """Decorator for async CLI commands with standard error handling.

    Usage:
        @async_command
        async def my_command(arg: str) -> None:
            async with authenticated_client() as (config, client):
                response = await client.some_method(arg)

    The decorator handles:
    - asyncio.run() wrapper
    - Exception catching and display_error()
    - typer.Exit(code=1) on failure

    Args:
        func: Async command function to wrap

    Returns:
        Wrapped sync function suitable for Typer command
    """

    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
        try:
            asyncio.run(func(*args, **kwargs))
        except (BlockingIOError, BrokenPipeError):
            # Pipe closed or blocked - exit silently
            # This happens when piping to programs like vim that don't immediately consume stdin
            raise typer.Exit(code=0)
        except typer.Exit:
            raise
        except Exception as e:
            display_error(e)
            raise typer.Exit(code=1)

    return wrapper


@asynccontextmanager
async def authenticated_client() -> AsyncGenerator[tuple[CLIConfig, ApiClient], None]:
    """Context manager for authenticated API operations.

    Usage:
        async with authenticated_client() as (config, client):
            response = await client.some_method()

    Handles:
    - Configuration loading (raises ConfigurationError if missing)
    - Client lifecycle management

    Yields:
        Tuple of (CLIConfig, ApiClient) for the operation

    Raises:
        ConfigurationError: If required configuration is missing
    """
    config = load_config()
    async with create_client(config) as client:
        yield config, client
