"""Server connection CLI commands.

Provides the ``agents server connect`` stub command and server-mode
status helpers.  All actual server communication is stubbed out —
commands persist configuration and print explicit warnings.
"""

from __future__ import annotations

from collections.abc import Callable
from pathlib import Path
from typing import Annotated, Any

import typer
from pydantic import ValidationError
from rich.console import Console
from rich.panel import Panel

from cleveragents.a2a.server_config import ServerConnectionConfig
from cleveragents.application.container import get_container
from cleveragents.application.services.config_service import ConfigService
from cleveragents.cli.formatting import OutputFormat, format_output

uvicorn_run: Callable[..., None] | None
try:
    from uvicorn import run as uvicorn_run
except ModuleNotFoundError:
    uvicorn_run = None

app: Any = typer.Typer(help="Server connection management (stub).")
console: Console = Console()

# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------

_STUB_WARNING: str = (
    "Server connection is not yet implemented. "
    "The URL has been saved to configuration but no connection will be made."
)

_LOG_LEVELS: tuple[str, ...] = (
    "critical",
    "error",
    "warning",
    "info",
    "debug",
    "trace",
)


def _normalize_log_level(value: str) -> str:
    """Normalize and validate ``--log-level`` values."""
    normalized = value.lower()
    if normalized not in _LOG_LEVELS:
        options = ", ".join(_LOG_LEVELS)
        raise typer.BadParameter(f"must be one of: {options}")
    return normalized


def _get_config_service() -> ConfigService:
    """Return a ``ConfigService`` wired to the standard config paths.

    Paths are computed lazily from ``Path.home()`` so that changes to
    ``HOME`` (e.g. in tests) are picked up correctly.
    """
    config_dir = Path.home() / ".cleveragents"
    config_path = config_dir / "config.toml"
    container = get_container()
    return ConfigService(
        config_dir=config_dir,
        config_path=config_path,
        event_bus=container.event_bus(),
    )


def resolve_server_mode() -> str:
    """Determine the current server mode.

    Returns:
        ``"disabled"`` when no server URL is configured, ``"stubbed"``
        when a URL is present but the client is not yet implemented.
    """
    svc = _get_config_service()
    try:
        resolved = svc.resolve("server.url")
        if resolved.value is not None and str(resolved.value).strip():
            return "stubbed"
    except (ValueError, KeyError):
        pass
    return "disabled"


# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------


@app.command("connect")
def server_connect(
    server_url: Annotated[
        str,
        typer.Argument(
            help="Server URL to connect to (e.g. https://agents.example.com)"
        ),
    ],
    namespace: Annotated[
        str,
        typer.Option("--namespace", "-n", help="Namespace on the server"),
    ] = "default",
    tls_verify: Annotated[
        bool,
        typer.Option("--tls-verify/--no-tls-verify", help="Verify TLS certificates"),
    ] = True,
    fmt: Annotated[
        str,
        typer.Option("--format", "-f", help="Output format: rich, plain, json, yaml"),
    ] = "rich",
) -> None:
    """Connect to a CleverAgents server (stub).

    Validates and persists the server URL, namespace, and TLS settings
    to the global configuration file.  No actual connection is made —
    server mode is not yet implemented.

    Examples::

        agents server connect https://agents.example.com
        agents server connect https://agents.example.com --namespace my-team
        agents server connect http://localhost:8080 --no-tls-verify
    """
    # Validate the connection config
    try:
        config = ServerConnectionConfig(
            server_url=server_url,
            namespace=namespace,
            tls_verify=tls_verify,
        )
    except ValidationError as exc:
        console.print(f"[red]Invalid server configuration:[/red] {exc}")
        raise typer.Exit(code=1) from exc

    # Persist to config file via set_value() so that each change emits
    # a CONFIG_CHANGED audit event.  Using write_config() would bypass
    # the EventBus and leave no audit trail for these security-relevant
    # settings (server URL, namespace, TLS verification).
    svc = _get_config_service()
    svc.set_value("server.url", config.server_url)
    svc.set_value("server.namespace", config.namespace)
    svc.set_value("server.tls-verify", config.tls_verify)

    result: dict[str, Any] = {
        "server_url": config.server_url,
        "namespace": config.namespace,
        "tls_verify": config.tls_verify,
        "status": "stubbed",
        "warning": _STUB_WARNING,
    }

    if fmt != OutputFormat.RICH.value:
        typer.echo(format_output(result, fmt))
        return

    console.print(
        Panel(
            f"[bold]Server URL:[/bold] {config.server_url}\n"
            f"[bold]Namespace:[/bold] {config.namespace}\n"
            f"[bold]TLS Verify:[/bold] {config.tls_verify}\n"
            f"[bold]Status:[/bold] stubbed\n"
            f"\n[yellow]{_STUB_WARNING}[/yellow]",
            title="Server Connection (Stub)",
            expand=False,
        )
    )


@app.command("status")
def server_status(
    fmt: Annotated[
        str,
        typer.Option("--format", "-f", help="Output format: rich, plain, json, yaml"),
    ] = "rich",
) -> None:
    """Show current server connection status.

    Displays the configured server URL (if any) and whether server mode
    is disabled or stubbed.
    """
    mode = resolve_server_mode()
    svc = _get_config_service()

    server_url: str | None = None
    namespace: str = "default"
    tls_verify: bool = True

    try:
        resolved_url = svc.resolve("server.url")
        if resolved_url.value is not None:
            server_url = str(resolved_url.value)
    except (ValueError, KeyError):
        pass

    try:
        resolved_ns = svc.resolve("server.namespace")
        if resolved_ns.value is not None:
            namespace = str(resolved_ns.value)
    except (ValueError, KeyError):
        pass

    try:
        resolved_tls = svc.resolve("server.tls-verify")
        if resolved_tls.value is not None:
            tls_verify = bool(resolved_tls.value)
    except (ValueError, KeyError):
        pass

    result: dict[str, Any] = {
        "server_mode": mode,
        "server_url": server_url,
        "namespace": namespace,
        "tls_verify": tls_verify,
    }

    if fmt != OutputFormat.RICH.value:
        typer.echo(format_output(result, fmt))
        return

    url_display = server_url if server_url else "(not configured)"
    console.print(
        Panel(
            f"[bold]Server Mode:[/bold] {mode}\n"
            f"[bold]Server URL:[/bold] {url_display}\n"
            f"[bold]Namespace:[/bold] {namespace}\n"
            f"[bold]TLS Verify:[/bold] {tls_verify}",
            title="Server Status",
            expand=False,
        )
    )


@app.command("serve")
def server_serve(
    host: Annotated[
        str,
        typer.Option("--host", help="Bind address for the ASGI server"),
    ] = "0.0.0.0",  # nosec B104
    port: Annotated[
        int,
        typer.Option("--port", min=1, max=65535, help="Bind port for the ASGI server"),
    ] = 8000,
    workers: Annotated[
        int,
        typer.Option("--workers", min=1, help="Number of uvicorn worker processes"),
    ] = 1,
    log_level: Annotated[
        str,
        typer.Option(
            "--log-level",
            help="Uvicorn log level",
            callback=_normalize_log_level,
        ),
    ] = "info",
    app_target: Annotated[
        str,
        typer.Option("--app", help="ASGI app import path (module:attribute)"),
    ] = "cleveragents.a2a.asgi:app",
) -> None:
    """Run the CleverAgents ASGI server process.

    This command is used by container runtimes (Docker/Kubernetes) to launch
    the server endpoint using the project CLI entrypoint (`python -m cleveragents`).
    """
    runner = uvicorn_run
    if runner is None:
        console.print(
            "[red]Unable to start server:[/red] optional dependency "
            "'uvicorn' is not installed."
        )
        raise typer.Exit(code=1)

    runner(
        app_target,
        host=host,
        port=port,
        workers=workers,
        log_level=log_level,
    )


__all__ = [
    "app",
    "resolve_server_mode",
    "server_connect",
    "server_serve",
    "server_status",
]
