"""Uvicorn server lifecycle management with graceful shutdown.

Provides :class:`ServerLifecycle` which wraps ``uvicorn.Server`` with
SIGTERM / SIGINT handling so that the server shuts down cleanly when
the process is terminated or the user presses Ctrl-C.

The public :func:`run_server` convenience function creates the ASGI
application, configures uvicorn, and blocks until shutdown.
"""

from __future__ import annotations

import signal
import threading
from types import FrameType

import structlog
import uvicorn

from cleveragents.a2a.facade import A2aLocalFacade
from cleveragents.config.settings import Settings
from cleveragents.infrastructure.server.asgi_app import create_asgi_app

logger: structlog.stdlib.BoundLogger = structlog.get_logger(__name__)


class ServerLifecycle:
    """Manage the lifecycle of the uvicorn ASGI server.

    Parameters
    ----------
    host:
        Network address to bind to.
    port:
        TCP port to listen on.
    facade:
        The A2A facade used for request dispatch.  *None* creates a
        default facade with no wired services.
    log_level:
        Uvicorn log level (``"info"``, ``"warning"``, etc.).
    """

    def __init__(
        self,
        *,
        host: str = "0.0.0.0",
        port: int = 8080,
        facade: A2aLocalFacade | None = None,
        log_level: str = "info",
    ) -> None:
        if not isinstance(host, str) or not host:
            raise ValueError("host must be a non-empty string")
        if not isinstance(port, int) or port < 1 or port > 65535:
            raise ValueError("port must be an integer between 1 and 65535")
        if not isinstance(log_level, str) or not log_level:
            raise ValueError("log_level must be a non-empty string")

        self._host: str = host
        self._port: int = port
        self._facade: A2aLocalFacade = (
            facade if facade is not None else A2aLocalFacade()
        )
        self._log_level: str = log_level.lower()
        self._server: uvicorn.Server | None = None
        self._started: bool = False
        self._stopped: bool = False

    # ------------------------------------------------------------------
    # Properties
    # ------------------------------------------------------------------

    @property
    def host(self) -> str:
        """Bound host address."""
        return self._host

    @property
    def port(self) -> int:
        """Bound port number."""
        return self._port

    @property
    def is_started(self) -> bool:
        """Whether :meth:`start` has been called."""
        return self._started

    @property
    def is_stopped(self) -> bool:
        """Whether the server has stopped."""
        return self._stopped

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    def start(self) -> None:
        """Build the ASGI app and run uvicorn (blocking).

        Installs SIGTERM/SIGINT handlers for graceful shutdown.  This
        method blocks until the server exits.
        """
        if self._started:
            raise RuntimeError("Server has already been started")

        self._started = True
        app = create_asgi_app(
            facade=self._facade,
            host=self._host,
            port=self._port,
        )

        config = uvicorn.Config(
            app=app,
            host=self._host,
            port=self._port,
            log_level=self._log_level,
        )
        server = uvicorn.Server(config)
        self._server = server

        logger.info(
            "a2a.server.starting",
            host=self._host,
            port=self._port,
        )

        # Install signal handlers for graceful shutdown.
        self._install_signal_handlers()

        server.run()
        self._stopped = True

        logger.info("a2a.server.stopped")

    def request_shutdown(self) -> None:
        """Request a graceful shutdown of the running server.

        Safe to call from signal handlers or other threads.
        """
        if self._server is not None:
            self._server.should_exit = True
            logger.info("a2a.server.shutdown_requested")

    # ------------------------------------------------------------------
    # Signal handling
    # ------------------------------------------------------------------

    def _install_signal_handlers(self) -> None:
        """Install SIGTERM and SIGINT handlers when running on main thread."""
        if threading.current_thread() is not threading.main_thread():
            return

        def _handle_signal(signum: int, _frame: FrameType | None) -> None:
            sig_name = signal.Signals(signum).name
            logger.info("a2a.server.signal_received", signal=sig_name)
            self.request_shutdown()

        signal.signal(signal.SIGTERM, _handle_signal)
        signal.signal(signal.SIGINT, _handle_signal)


# ------------------------------------------------------------------
# Convenience runner
# ------------------------------------------------------------------


def run_server(  # pragma: no cover
    *,
    host: str | None = None,
    port: int | None = None,
    facade: A2aLocalFacade | None = None,
    log_level: str | None = None,
) -> None:
    """Create and run the ASGI server (blocking).

    Settings are resolved from :class:`Settings` when parameters are
    *None*.

    Parameters
    ----------
    host:
        Override for ``Settings.server_host``.
    port:
        Override for ``Settings.server_port``.
    facade:
        A2A facade for operation dispatch.
    log_level:
        Override for ``Settings.log_level``.
    """
    settings = Settings.get_settings()

    resolved_host = host if host is not None else settings.server_host
    resolved_port = port if port is not None else settings.server_port
    resolved_log_level = log_level if log_level is not None else settings.log_level

    lifecycle = ServerLifecycle(
        host=resolved_host,
        port=resolved_port,
        facade=facade,
        log_level=resolved_log_level,
    )
    lifecycle.start()


__all__ = [
    "ServerLifecycle",
    "run_server",
]
