"""Lightweight utilities for managing vLLM server lifecycle.

Optional utilities for GenAI agents and automated testing.
Not required for normal scicode-lint usage.

Supports both local and remote vLLM servers:
    - **Local servers**: Can start/stop programmatically
    - **Remote servers**: Can verify connectivity only

Features:
    - Start/stop local vLLM servers programmatically
    - Check server status and GPU information
    - Context manager for automatic lifecycle management
    - Support for remote vLLM servers

Example - Local Server Management:
    >>> from scicode_lint.vllm import start_server, stop_server, wait_for_ready
    >>> proc = start_server()
    >>> if wait_for_ready():
    ...     linter = SciCodeLinter()
    ...     result = linter.check_file(Path("myfile.py"))
    >>> stop_server(proc)

Example - Context Manager (Local):
    >>> from scicode_lint.vllm import VLLMServer
    >>> with VLLMServer():  # Auto-starts if needed
    ...     linter = SciCodeLinter()
    ...     result = linter.check_file(Path("myfile.py"))

Example - Remote Server:
    >>> with VLLMServer(base_url="http://gpu-cluster:5001"):
    ...     linter = SciCodeLinter()  # Uses remote server
    ...     result = linter.check_file(Path("myfile.py"))

Example - System Info:
    >>> from scicode_lint.vllm import get_gpu_info, print_system_info
    >>> gpu = get_gpu_info()
    >>> print(f"Free VRAM: {gpu.free_memory_mb} MB")
    >>> print_system_info()  # Print complete system status

Also includes start_vllm.sh bash script for manual server startup.
"""

import asyncio
import subprocess
import time
import types
from dataclasses import dataclass
from datetime import UTC
from typing import Any

import httpx

# Fallback model name (used only if model not specified in config)
_DEFAULT_MODEL_FALLBACK = "RedHatAI/Qwen3-8B-FP8-dynamic"


def _get_vllm_config() -> dict[str, Any]:
    """Get vLLM config from config.toml. Fails if config cannot be loaded."""
    from scicode_lint.config import load_config_from_toml

    config = load_config_from_toml()
    result: dict[str, Any] = config.get("vllm", {})
    return result


def _get_llm_config_value(key: str, default: Any) -> Any:
    """Get a value from [llm] section of config.toml. Fails if config cannot be loaded."""
    from scicode_lint.config import load_config_from_toml

    config = load_config_from_toml()
    return config.get("llm", {}).get(key, default)


def _get_default_model() -> str:
    """Get default model from config.toml or fallback."""
    result: str = _get_llm_config_value("model", _DEFAULT_MODEL_FALLBACK)
    return result


def _get_default_max_model_len() -> int:
    """Get max_model_len from config.toml (computed from max_input + max_completion)."""
    max_input: int = _get_llm_config_value("max_input_tokens", 16000)
    max_completion: int = _get_llm_config_value("max_completion_tokens", 4096)
    return max_input + max_completion


def _get_min_vram_mb() -> int:
    """Get minimum VRAM requirement from config.toml."""
    vllm_config = _get_vllm_config()
    if "min_vram_mb" not in vllm_config:
        raise KeyError("min_vram_mb not found in [vllm] section of config.toml")
    return int(vllm_config["min_vram_mb"])


def _get_gpu_memory_utilization() -> float:
    """Get GPU memory utilization from config.toml."""
    vllm_config = _get_vllm_config()
    if "gpu_memory_utilization" not in vllm_config:
        raise KeyError("gpu_memory_utilization not found in [vllm] section of config.toml")
    return float(vllm_config["gpu_memory_utilization"])


def is_running(base_url: str = "http://localhost:5001") -> bool:
    """Check if vLLM server is running.

    Args:
        base_url: Server URL to check (default: http://localhost:5001)

    Returns:
        True if server is responding, False otherwise

    Example:
        >>> from scicode_lint.vllm import is_running
        >>> if is_running():
        ...     print("Server is ready")
    """
    try:
        response = httpx.get(f"{base_url}/health", timeout=2)
        return response.status_code == 200
    except (httpx.HTTPError, ConnectionError):
        return False


def wait_for_ready(
    base_url: str = "http://localhost:5001",
    timeout: int = 60,
    check_interval: float = 2.0,
) -> bool:
    """Wait for vLLM server to be ready.

    Args:
        base_url: Server URL to check (default: http://localhost:5001)
        timeout: Maximum time to wait in seconds (default: 60)
        check_interval: Time between checks in seconds (default: 2.0)

    Returns:
        True if server became ready, False if timeout reached

    Example:
        >>> from scicode_lint.vllm import wait_for_ready
        >>> if wait_for_ready(timeout=30):
        ...     print("Server is ready")
        ... else:
        ...     print("Timeout waiting for server")
    """
    start_time = time.time()
    while time.time() - start_time < timeout:
        if is_running(base_url):
            return True
        time.sleep(check_interval)
    return False


def _check_vllm_version() -> None:
    """Check if vLLM version is 0.16+ and raise if not."""
    try:
        result = subprocess.run(
            ["vllm", "--version"],
            capture_output=True,
            text=True,
            check=False,
        )
        # Parse version from output like "vllm 0.16.0"
        import re

        match = re.search(r"vllm (\d+)\.(\d+)", result.stdout + result.stderr)
        if match:
            major, minor = int(match.group(1)), int(match.group(2))
            if major == 0 and minor < 16:
                raise RuntimeError(
                    f"vLLM version 0.{minor}.x is too old (requires 0.16+). "
                    "Upgrade with: pip install --upgrade vllm"
                )
    except (FileNotFoundError, subprocess.CalledProcessError):
        # vllm command not found or failed - will be caught by start_server
        pass


def _auto_detect_vram_settings(override_vram_mb: int | None = None) -> tuple[int, float]:
    """Verify VRAM requirements and return settings from config.

    Args:
        override_vram_mb: Override VRAM detection for testing (e.g., 16384 for 16GB)

    Returns:
        Tuple of (max_model_len, gpu_memory_utilization) from config.toml
    """
    # Get config values
    min_vram_mb = _get_min_vram_mb()
    max_model_len = _get_default_max_model_len()
    gpu_mem_util = _get_gpu_memory_utilization()

    if override_vram_mb is not None:
        vram_mb = override_vram_mb
    else:
        gpu_info = get_gpu_info()
        if gpu_info is None:
            # Cannot detect VRAM - require user to specify
            min_vram_gb = min_vram_mb // 1024
            raise RuntimeError(
                "Cannot detect GPU VRAM. Please ensure nvidia-smi is available.\n"
                f"Minimum requirement: {min_vram_gb}GB VRAM with native FP8 support"
            )
        vram_mb = gpu_info.total_memory_mb

    # Enforce minimum VRAM from config
    if vram_mb < min_vram_mb:
        vram_gb = vram_mb // 1024
        min_vram_gb = min_vram_mb // 1024
        raise RuntimeError(
            f"Detected {vram_gb}GB VRAM. Minimum requirement: {min_vram_gb}GB VRAM.\n"
            "\n"
            f"scicode-lint requires {min_vram_gb}GB+ VRAM with native FP8 support.\n"
            "Supported GPUs (compute capability >= 8.9):\n"
            "  • Consumer: RTX 4060 Ti 16GB, RTX 4070+ (16GB+), RTX 4090 (24GB)\n"
            "  • Workstation: RTX 4000 Ada (20GB), RTX 5000 Ada (32GB)\n"
            "  • Cloud/HPC inference: L4 (24GB), L40 (48GB), A10 (24GB)\n"
            "\n"
            "See INSTALLATION.md for deployment options."
        )

    # Return settings from config
    # max_model_len: total context (input + output)
    # gpu_mem_util: fraction of VRAM to use
    return max_model_len, gpu_mem_util


def start_server(
    model: str | None = None,
    port: int = 5001,
    max_model_len: int | None = None,
    gpu_memory_utilization: float | None = None,
    wait: bool = False,
    wait_timeout: int = 60,
) -> subprocess.Popen[bytes]:
    """Start vLLM server as a subprocess.

    Args:
        model: Model name or path (default: from config.toml)
        port: Port to run on (default: 5001)
        max_model_len: Maximum context length (default: ~20K tokens)
        gpu_memory_utilization: GPU memory to use 0.0-1.0 (default: from config.toml)
        wait: Wait for server to be ready before returning (default: False)
        wait_timeout: Timeout for waiting in seconds (default: 60)

    Returns:
        subprocess.Popen object representing the server process

    Raises:
        FileNotFoundError: If vllm command not found
        TimeoutError: If wait=True and server doesn't start in time
        RuntimeError: If server is already running on the port

    Example:
        >>> from scicode_lint.vllm import start_server, stop_server
        >>> proc = start_server(wait=True)  # Auto-detects VRAM settings
        >>> try:
        ...     # Use linter
        ...     linter = SciCodeLinter()
        ...     result = linter.check_file(Path("myfile.py"))
        ... finally:
        ...     stop_server(proc)

    Note:
        Model will download automatically on first run (~13GB).
        Server output goes to stdout/stderr unless redirected.
    """
    # Check vLLM version
    _check_vllm_version()

    # Use default model from config if not specified
    if model is None:
        model = _get_default_model()

    # Check if already running
    if is_running(f"http://localhost:{port}"):
        raise RuntimeError(
            f"vLLM server already running on port {port}\n\n"
            f"Suggestions:\n"
            f"  • Use existing server: scicode-lint will auto-detect it\n"
            f"  • Stop server: Use stop_server() or kill the vllm process\n"
            f"  • Use different port: start_server(port={port + 1})"
        )

    # Auto-detect settings if not specified
    if max_model_len is None or gpu_memory_utilization is None:
        auto_len, auto_mem = _auto_detect_vram_settings()
        if max_model_len is None:
            max_model_len = auto_len
        if gpu_memory_utilization is None:
            gpu_memory_utilization = auto_mem

    # Build command (model is positional arg after 'serve')
    cmd = [
        "vllm",
        "serve",
        model,
        "--host",
        "0.0.0.0",
        "--port",
        str(port),
        "--trust-remote-code",
        "--gpu-memory-utilization",
        str(gpu_memory_utilization),
        "--max-model-len",
        str(max_model_len),
    ]

    # Start process (output goes to /dev/null — use vLLM's own logging)
    try:
        proc = subprocess.Popen(
            cmd,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )
    except FileNotFoundError:
        raise FileNotFoundError(
            "vllm command not found. Install with: pip install scicode-lint[vllm-server]"
        )

    # Wait for ready if requested
    if wait:
        if not wait_for_ready(f"http://localhost:{port}", timeout=wait_timeout):
            stop_server(proc)
            raise TimeoutError(f"Server failed to start within {wait_timeout} seconds")

    return proc


def stop_server(process: subprocess.Popen[bytes], timeout: int = 10) -> None:
    """Stop vLLM server process gracefully.

    Args:
        process: Process object returned by start_server()
        timeout: Timeout for graceful shutdown in seconds (default: 10)

    Example:
        >>> from scicode_lint.vllm import start_server, stop_server
        >>> proc = start_server()
        >>> # ... use server ...
        >>> stop_server(proc)

    Note:
        Tries graceful termination first, then kills if necessary.
    """
    if process.poll() is None:  # Process still running
        process.terminate()
        try:
            process.wait(timeout=timeout)
        except subprocess.TimeoutExpired:
            process.kill()
            process.wait()


class VLLMServer:
    """Context manager for vLLM server lifecycle.

    Supports both local and remote vLLM servers:
    - **Local server (default)**: Starts if not running, stops if we started it
    - **Remote server**: Only verifies it's reachable, never starts/stops

    Args:
        model: Model name or path (only used for local servers)
        port: Port to run on (only used for local servers)
        base_url: Full URL for remote server (e.g., "http://10.0.0.5:5001")
                  If provided, port is ignored and no start/stop attempted
        max_model_len: Maximum context length (local servers only, default: ~20K)
        gpu_memory_utilization: GPU memory 0.0-1.0 (local servers only, default: from config.toml)
        wait_timeout: Timeout for server startup/verification in seconds

    Example - Local server (auto-start):
        >>> from scicode_lint.vllm import VLLMServer
        >>> from scicode_lint import SciCodeLinter
        >>>
        >>> # Starts local server if not running, stops when done
        >>> with VLLMServer():
        ...     linter = SciCodeLinter()
        ...     result = linter.check_file(Path("myfile.py"))

    Example - Remote server:
        >>> # Just verifies remote server is reachable
        >>> with VLLMServer(base_url="http://gpu-cluster.example.com:5001"):
        ...     linter = SciCodeLinter()
        ...     result = linter.check_file(Path("myfile.py"))
    """

    def __init__(
        self,
        model: str | None = None,
        port: int = 5001,
        base_url: str | None = None,
        max_model_len: int | None = None,
        gpu_memory_utilization: float | None = None,
        wait_timeout: int = 60,
    ):
        """Initialize VLLMServer context manager.

        Uses settings from config.toml: 20K context, GPU memory utilization.
        Model defaults to value from config.toml.
        """
        self.model = model if model is not None else _get_default_model()
        self.port = port
        self.base_url = base_url

        # Verify VRAM requirements and use standard settings (only for local servers)
        if base_url is None and (max_model_len is None or gpu_memory_utilization is None):
            auto_len, auto_mem = _auto_detect_vram_settings()
            self.max_model_len = max_model_len if max_model_len is not None else auto_len
            self.gpu_memory_utilization = (
                gpu_memory_utilization if gpu_memory_utilization is not None else auto_mem
            )
        else:
            # Use provided values or fallback to config defaults
            self.max_model_len = (
                max_model_len if max_model_len is not None else _get_default_max_model_len()
            )
            self.gpu_memory_utilization = (
                gpu_memory_utilization
                if gpu_memory_utilization is not None
                else _get_gpu_memory_utilization()
            )

        self.wait_timeout = wait_timeout
        self.process: subprocess.Popen[bytes] | None = None
        self.was_already_running = False
        self.is_remote = base_url is not None

    def __enter__(self) -> "VLLMServer":
        """Start vLLM server if local and not already running."""
        server_url = self.base_url or f"http://localhost:{self.port}"

        # Check if server already running
        if is_running(server_url):
            self.was_already_running = True

            # Warn if running model differs from requested (local only)
            if not self.is_remote:
                try:
                    response = httpx.get(f"{server_url}/v1/models", timeout=5)
                    if response.status_code == 200:
                        data = response.json()
                        if "data" in data and len(data["data"]) > 0:
                            running_model = data["data"][0].get("id", "unknown")
                            # Check if models differ (ignoring served-model-name aliases)
                            if self.model not in running_model and running_model not in self.model:
                                import warnings

                                msg = (
                                    f"Local vLLM server is running with model "
                                    f"'{running_model}' but you requested '{self.model}'. "
                                    f"Using existing server. To use the requested model, "
                                    f"stop the server first: pkill -f 'vllm serve'"
                                )
                                warnings.warn(msg, RuntimeWarning)
                except (httpx.HTTPError, KeyError, IndexError):
                    pass  # Can't verify model, proceed anyway

            return self

        # Remote server not reachable
        if self.is_remote:
            raise RuntimeError(
                f"Remote vLLM server not reachable at {server_url}. "
                "Cannot start remote servers - please start it manually."
            )

        # Start local server
        self.process = start_server(
            model=self.model,
            port=self.port,
            max_model_len=self.max_model_len,
            gpu_memory_utilization=self.gpu_memory_utilization,
            wait=True,
            wait_timeout=self.wait_timeout,
        )
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: types.TracebackType | None,
    ) -> None:
        """Stop vLLM server only if we started it (local only)."""
        # Only stop if we started the server (don't kill existing/remote servers)
        if self.process and not self.was_already_running and not self.is_remote:
            stop_server(self.process)


@dataclass
class GPUInfo:
    """GPU information from nvidia-smi.

    Attributes:
        name: GPU name (e.g., "NVIDIA RTX 4000 Ada")
        total_memory_mb: Total VRAM in MB
        used_memory_mb: Used VRAM in MB
        free_memory_mb: Free VRAM in MB
        utilization_percent: GPU utilization percentage
        cuda_version: CUDA version
    """

    name: str
    total_memory_mb: int
    used_memory_mb: int
    free_memory_mb: int
    utilization_percent: int
    cuda_version: str


@dataclass
class ServerInfo:
    """vLLM server information.

    Attributes:
        model: Model name being served
        is_running: Whether server is responding
        base_url: Server URL
        max_model_len: Maximum context length configured on server
    """

    model: str | None
    is_running: bool
    base_url: str
    max_model_len: int | None = None


def get_gpu_info() -> GPUInfo | None:
    """Get GPU information using nvidia-smi.

    Returns:
        GPUInfo object with GPU details, or None if nvidia-smi fails

    Example:
        >>> from scicode_lint.vllm import get_gpu_info
        >>> gpu = get_gpu_info()
        >>> if gpu:
        ...     print(f"GPU: {gpu.name}")
        ...     print(f"VRAM: {gpu.used_memory_mb}/{gpu.total_memory_mb} MB")
        ...     print(f"Free: {gpu.free_memory_mb} MB")
        ...     print(f"Utilization: {gpu.utilization_percent}%")
    """
    try:
        result = subprocess.run(
            [
                "nvidia-smi",
                "--query-gpu=name,memory.total,memory.used,memory.free,utilization.gpu",
                "--format=csv,noheader,nounits",
            ],
            capture_output=True,
            text=True,
            check=True,
        )

        # Parse first GPU (most systems have one)
        line = result.stdout.strip().split("\n")[0]
        parts = [p.strip() for p in line.split(",")]

        # Get CUDA version
        cuda_result = subprocess.run(
            ["nvidia-smi", "--query-gpu=driver_version", "--format=csv,noheader"],
            capture_output=True,
            text=True,
            check=True,
        )
        cuda_version = cuda_result.stdout.strip()

        return GPUInfo(
            name=parts[0],
            total_memory_mb=int(parts[1]),
            used_memory_mb=int(parts[2]),
            free_memory_mb=int(parts[3]),
            utilization_percent=int(parts[4]),
            cuda_version=cuda_version,
        )
    except (subprocess.CalledProcessError, FileNotFoundError, IndexError, ValueError):
        return None


def get_server_info(base_url: str = "http://localhost:5001") -> ServerInfo:
    """Get vLLM server information.

    Args:
        base_url: Server URL (default: http://localhost:5001)

    Returns:
        ServerInfo object with server details

    Example:
        >>> from scicode_lint.vllm import get_server_info
        >>> info = get_server_info()
        >>> if info.is_running:
        ...     print(f"Model: {info.model}")
        ...     print(f"Max context: {info.max_model_len}")
        >>> else:
        ...     print("Server not running")
    """
    model_name = None
    max_model_len = None
    running = is_running(base_url)

    if running:
        try:
            # Try to get model info from /v1/models endpoint
            response = httpx.get(f"{base_url}/v1/models", timeout=5)
            if response.status_code == 200:
                data = response.json()
                if "data" in data and len(data["data"]) > 0:
                    model_info = data["data"][0]
                    model_name = model_info.get("id")
                    max_model_len = model_info.get("max_model_len")
        except (httpx.HTTPError, KeyError, IndexError):
            pass

    return ServerInfo(
        model=model_name,
        is_running=running,
        base_url=base_url,
        max_model_len=max_model_len,
    )


class VLLMMetricsMonitor:
    """Background monitor for vLLM metrics during long-running operations.

    Periodically fetches vLLM server metrics (running requests, queued requests,
    throughput) and writes them to a file for later analysis.

    Args:
        base_url: vLLM server URL (default: http://localhost:5001)
        interval: Seconds between metric checks (default: 5.0)
        output_file: Path to write metrics (default: evals/reports/vllm_metrics.log)
        console: Also print summary to console (default: True)

    Example:
        >>> import asyncio
        >>> from scicode_lint.vllm import VLLMMetricsMonitor
        >>>
        >>> async def run_with_monitoring():
        ...     monitor = VLLMMetricsMonitor()
        ...     monitor.start()
        ...     try:
        ...         await asyncio.sleep(30)
        ...     finally:
        ...         await monitor.stop()
        ...         print(f"Metrics saved to {monitor.output_file}")
    """

    def __init__(
        self,
        base_url: str = "http://localhost:5001",
        interval: float = 5.0,
        output_file: str | None = None,
        console: bool = True,
    ):
        self.metrics_url = f"{base_url}/metrics"
        self.interval = interval
        self.output_file = output_file or "evals/reports/vllm_metrics.log"
        self.console = console
        self._task: asyncio.Task[Any] | None = None
        self._stop = False
        self._start_time = 0.0
        self._peak_running = 0
        self._peak_waiting = 0
        self._total_requests = 0

    async def _fetch_metrics(self) -> dict[str, float] | None:
        """Fetch and parse vLLM metrics."""
        try:
            async with httpx.AsyncClient(timeout=2.0) as client:
                resp = await client.get(self.metrics_url)
                if resp.status_code != 200:
                    return None

                metrics = {}
                for line in resp.text.split("\n"):
                    if line.startswith("vllm:num_requests_running"):
                        metrics["running"] = float(line.split()[-1])
                    elif line.startswith("vllm:num_requests_waiting"):
                        metrics["waiting"] = float(line.split()[-1])
                    elif line.startswith("vllm:num_requests_finished"):
                        metrics["finished"] = float(line.split()[-1])
                return metrics
        except Exception:
            return None

    async def _monitor_loop(self) -> None:
        """Background loop collecting metrics."""
        import os
        from datetime import datetime

        self._start_time = time.time()
        last_finished = 0.0

        # Ensure output directory exists
        os.makedirs(os.path.dirname(self.output_file), exist_ok=True)

        with open(self.output_file, "w") as f:
            # Write CSV header
            f.write("timestamp,elapsed_s,running,waiting,finished,throughput_req_s\n")
            f.flush()

            while not self._stop:
                metrics = await self._fetch_metrics()
                if metrics:
                    now = datetime.now(UTC).isoformat(timespec="seconds")
                    elapsed = time.time() - self._start_time
                    running = int(metrics.get("running", 0))
                    waiting = int(metrics.get("waiting", 0))
                    finished = metrics.get("finished", 0)

                    # Track peaks
                    self._peak_running = max(self._peak_running, running)
                    self._peak_waiting = max(self._peak_waiting, waiting)
                    self._total_requests = int(finished)

                    # Calculate throughput
                    completed_delta = finished - last_finished
                    last_finished = finished
                    throughput = completed_delta / self.interval if self.interval > 0 else 0

                    # Write to file (CSV format with timestamp)
                    f.write(
                        f"{now},{elapsed:.1f},{running},{waiting},{finished:.0f},{throughput:.2f}\n"
                    )
                    f.flush()

                    # Optional console output
                    if self.console:
                        print(
                            f"[{elapsed:5.0f}s] vLLM: {running} running, {waiting} queued, "
                            f"{throughput:.1f} req/s"
                        )

                await asyncio.sleep(self.interval)

    def start(self) -> None:
        """Start background monitoring."""
        self._stop = False
        self._peak_running = 0
        self._peak_waiting = 0
        self._total_requests = 0
        self._task = asyncio.create_task(self._monitor_loop())

    async def stop(self) -> None:
        """Stop background monitoring and return summary."""
        self._stop = True
        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass

        elapsed = time.time() - self._start_time
        if self.console and elapsed > 0:
            avg_throughput = self._total_requests / elapsed if elapsed > 0 else 0
            print(
                f"Metrics summary: peak {self._peak_running} concurrent, "
                f"{self._peak_waiting} max queued, {avg_throughput:.1f} avg req/s"
            )
            print(f"Full metrics: {self.output_file}")


def print_system_info() -> None:
    """Print system information (GPU, server status).

    Useful for GenAI agents to check system readiness before starting work.

    Example:
        >>> from scicode_lint.vllm import print_system_info
        >>> print_system_info()
        === GPU Information ===
        GPU: NVIDIA RTX 4000 Ada
        VRAM: 4096/20480 MB (20% used, 16384 MB free)
        Utilization: 15%
        CUDA: 535.183.01

        === vLLM Server ===
        Status: Running
        Model: RedHatAI/Qwen3-8B-FP8-dynamic
        URL: http://localhost:5001
    """
    print("=== GPU Information ===")
    gpu = get_gpu_info()
    if gpu:
        print(f"GPU: {gpu.name}")
        print(
            f"VRAM: {gpu.used_memory_mb}/{gpu.total_memory_mb} MB "
            f"({gpu.used_memory_mb * 100 // gpu.total_memory_mb}% used, "
            f"{gpu.free_memory_mb} MB free)"
        )
        print(f"Utilization: {gpu.utilization_percent}%")
        print(f"CUDA: {gpu.cuda_version}")
    else:
        print("GPU information not available (nvidia-smi failed)")

    print("\n=== vLLM Server ===")
    server = get_server_info()
    if server.is_running:
        print("Status: Running")
        print(f"Model: {server.model or 'Unknown'}")
        print(f"URL: {server.base_url}")
    else:
        print("Status: Not running")


__all__ = [
    "start_server",
    "stop_server",
    "wait_for_ready",
    "is_running",
    "VLLMServer",
    "VLLMMetricsMonitor",
    "get_gpu_info",
    "get_server_info",
    "print_system_info",
    "GPUInfo",
    "ServerInfo",
]
