"""HTTP server for Prometheus metrics endpoint."""

from __future__ import annotations

import logging
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
from typing import TYPE_CHECKING

from gaol.metrics.exceptions import (
    MetricsServerAlreadyRunningError,
    MetricsServerError,
    MetricsServerNotRunningError,
)
from gaol.metrics.exporter import PrometheusExporter, get_prometheus_exporter
from gaol.metrics.models import MetricsServerConfig
from gaol.metrics.store import get_metrics_store

if TYPE_CHECKING:
    from gaol.metrics.store import MetricsStore

logger = logging.getLogger(__name__)


class MetricsHTTPHandler(BaseHTTPRequestHandler):
    """HTTP request handler for /metrics endpoint."""

    # Class-level reference to exporter, set by MetricsServer
    exporter: PrometheusExporter | None = None

    def log_message(self, format: str, *args) -> None:
        """Override to use logging instead of stderr."""
        logger.debug(f"HTTP: {format % args}")

    def do_GET(self) -> None:
        """Handle GET requests."""
        if self.path == "/metrics" or self.path == "/":
            self._handle_metrics()
        elif self.path == "/health" or self.path == "/healthz":
            self._handle_health()
        else:
            self.send_error(404, "Not Found")

    def _handle_metrics(self) -> None:
        """Serve Prometheus metrics."""
        if self.exporter is None:
            self.send_error(500, "Exporter not configured")
            return

        try:
            metrics = self.exporter.generate_metrics()
            self.send_response(200)
            self.send_header("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
            self.send_header("Content-Length", str(len(metrics)))
            self.end_headers()
            self.wfile.write(metrics)
        except Exception as e:
            logger.error(f"Error generating metrics: {e}")
            self.send_error(500, "Internal Server Error")

    def _handle_health(self) -> None:
        """Health check endpoint."""
        response = b"OK"
        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.send_header("Content-Length", str(len(response)))
        self.end_headers()
        self.wfile.write(response)


class MetricsServer:
    """Background HTTP server for Prometheus metrics.

    Runs a metrics collection loop and HTTP server in background threads.
    Follows the daemon thread pattern used by backup/scheduler.py.

    Example:
        >>> server = MetricsServer()
        >>> server.start()  # Start in background
        >>> # ... server runs ...
        >>> server.stop()
    """

    def __init__(
        self,
        config: MetricsServerConfig | None = None,
        exporter: PrometheusExporter | None = None,
        store: MetricsStore | None = None,
    ) -> None:
        """Initialize the metrics server.

        Args:
            config: Server configuration (loads from store if None)
            exporter: Prometheus exporter (uses global instance if None)
            store: Metrics store (uses global instance if None)
        """
        self._store = store or get_metrics_store()
        self._config = config or self._store.get_config()
        self._exporter = exporter or get_prometheus_exporter()

        self._running = False
        self._http_server: HTTPServer | None = None
        self._http_thread: threading.Thread | None = None
        self._collect_thread: threading.Thread | None = None
        self._lock = threading.Lock()

    @property
    def config(self) -> MetricsServerConfig:
        """Get the current server configuration."""
        return self._config

    @property
    def is_running(self) -> bool:
        """Check if the server is currently running."""
        return self._running

    @property
    def exporter(self) -> PrometheusExporter:
        """Get the Prometheus exporter."""
        return self._exporter

    def start(self) -> None:
        """Start the metrics server in background threads.

        Raises:
            MetricsServerAlreadyRunningError: If server is already running
            MetricsServerError: If server fails to start
        """
        with self._lock:
            if self._running:
                raise MetricsServerAlreadyRunningError(
                    self._config.host, self._config.port
                )

            try:
                # Configure HTTP handler with exporter reference
                MetricsHTTPHandler.exporter = self._exporter

                # Create HTTP server
                self._http_server = HTTPServer(
                    (self._config.host, self._config.port),
                    MetricsHTTPHandler,
                )
                self._http_server.timeout = 1.0

                self._running = True

                # Start HTTP server thread
                self._http_thread = threading.Thread(
                    target=self._run_http_server,
                    name="metrics-http",
                    daemon=True,
                )
                self._http_thread.start()

                # Start collection thread
                self._collect_thread = threading.Thread(
                    target=self._run_collection_loop,
                    name="metrics-collector",
                    daemon=True,
                )
                self._collect_thread.start()

                logger.info(
                    f"Metrics server started on http://{self._config.host}:{self._config.port}/metrics"
                )

            except OSError as e:
                self._running = False
                self._http_server = None
                raise MetricsServerError(
                    f"Failed to bind to {self._config.host}:{self._config.port}: {e}",
                    cause=e,
                )

    def stop(self, timeout: float = 5.0) -> None:
        """Stop the metrics server.

        Args:
            timeout: Maximum seconds to wait for threads to stop

        Raises:
            MetricsServerNotRunningError: If server is not running
        """
        with self._lock:
            if not self._running:
                raise MetricsServerNotRunningError()

            self._running = False

            # Shutdown HTTP server
            if self._http_server:
                self._http_server.shutdown()

        # Wait for threads (outside lock to avoid deadlock)
        if self._http_thread and self._http_thread.is_alive():
            self._http_thread.join(timeout=timeout)

        if self._collect_thread and self._collect_thread.is_alive():
            self._collect_thread.join(timeout=timeout)

        self._http_server = None
        self._http_thread = None
        self._collect_thread = None

        logger.info("Metrics server stopped")

    def _run_http_server(self) -> None:
        """Run the HTTP server loop."""
        if self._http_server is None:
            return

        try:
            self._http_server.serve_forever()
        except Exception as e:
            if self._running:
                logger.error(f"HTTP server error: {e}")

    def _run_collection_loop(self) -> None:
        """Run the metrics collection loop."""
        # Initial collection
        try:
            self._exporter.collect(self._config.containers)
        except Exception as e:
            logger.error(f"Initial metrics collection failed: {e}")

        # Periodic collection
        while self._running:
            time.sleep(self._config.refresh_interval)
            if not self._running:
                break

            try:
                self._exporter.collect(self._config.containers)
            except Exception as e:
                logger.error(f"Metrics collection failed: {e}")

    def collect_once(self) -> None:
        """Manually trigger a metrics collection.

        Useful for one-shot exports without starting the server.
        """
        self._exporter.collect(self._config.containers)


# Global server instance
_server: MetricsServer | None = None


def get_metrics_server() -> MetricsServer:
    """Get the global metrics server instance."""
    global _server
    if _server is None:
        _server = MetricsServer()
    return _server


def start_metrics_server(
    host: str | None = None,
    port: int | None = None,
    interval: float | None = None,
    containers: list[str] | None = None,
) -> MetricsServer:
    """Start the global metrics server with optional config overrides.

    Args:
        host: Override bind address
        port: Override port number
        interval: Override refresh interval
        containers: Override container filter

    Returns:
        The started MetricsServer instance

    Raises:
        MetricsServerAlreadyRunningError: If already running
        MetricsServerError: If server fails to start
    """
    global _server

    # Build config with overrides
    store = get_metrics_store()
    config = store.get_config()

    if host is not None:
        config.host = host
    if port is not None:
        config.port = port
    if interval is not None:
        config.refresh_interval = interval
    if containers is not None:
        config.containers = containers

    _server = MetricsServer(config=config)
    _server.start()
    return _server


def stop_metrics_server() -> None:
    """Stop the global metrics server.

    Raises:
        MetricsServerNotRunningError: If not running
    """
    global _server
    if _server is None:
        raise MetricsServerNotRunningError()
    _server.stop()
    _server = None
