"""Service to automatically monitor and restart local LM Studio server."""

from __future__ import annotations

import asyncio
import logging
import shutil
import subprocess

import httpx

from app.core.settings import get_settings

logger = logging.getLogger("dscons.lmstudio_manager")


class LMStudioManager:
    """Manages the lifecycle of the local LM Studio server using 'lms' CLI."""

    def __init__(self, check_url: str = "http://127.0.0.1:1234/v1/models") -> None:
        self.check_url = check_url
        self.settings = get_settings()

    async def is_server_running(self) -> bool:
        """Pings the LM Studio models endpoint to check if it's alive."""
        try:
            async with httpx.AsyncClient(timeout=3.0) as client:
                res = await client.get(self.check_url)
                return res.status_code == 200
        except Exception:
            return False

    async def start_server(self) -> bool:
        """Invokes the 'lms server start' command to start the server."""
        if not shutil.which("lms"):
            logger.error("lms CLI is not installed or not in PATH.")
            return False

        logger.info("Starting LM Studio server via 'lms server start'...")
        try:
            # Run 'lms server start' as a non-blocking process
            process = await asyncio.create_subprocess_exec(
                "lms", "server", "start", stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
            stdout, stderr = await process.communicate()
            if process.returncode == 0:
                logger.info("Successfully requested LM Studio server to start.")
                return True
            else:
                logger.error(
                    "Failed to start LM Studio: %s",
                    stderr.decode("utf-8", errors="ignore"),
                )
                return False
        except Exception as e:
            logger.error("Error executing 'lms server start': %s", e)
            return False

    async def check_and_recover(self) -> None:
        """Checks the server status and attempts recovery if down."""
        # Only check if LM Studio is enabled
        if not self.settings.lmstudio_enabled:
            return

        # Check if any agent is routed to lmstudio, or if it is the default provider
        uses_lmstudio = self.settings.llm_provider == "lmstudio" or any(
            prov == "lmstudio" for prov in self.settings.agent_provider_routing.values()
        )
        if not uses_lmstudio:
            return

        if not await self.is_server_running():
            logger.warning(
                "LM Studio server is NOT running. Attempting auto-restart..."
            )
            success = await self.start_server()
            if success:
                # Wait up to 10 seconds for the port to bind
                for _ in range(10):
                    await asyncio.sleep(1)
                    if await self.is_server_running():
                        logger.info("LM Studio server has recovered and is now active.")
                        return
                logger.error(
                    "LM Studio requested to start, but did not respond on port in time."
                )
            else:
                logger.error("Failed to execute LM Studio server auto-restart.")
        else:
            logger.debug("LM Studio server is active and healthy.")


async def start_lmstudio_monitor_loop() -> None:
    """Background loop that runs indefinitely checking and recovering LM Studio."""
    manager = LMStudioManager()
    while True:
        try:
            await manager.check_and_recover()
        except Exception as e:
            logger.error("Error in LM Studio monitor loop: %s", e)
        # Check every 60 seconds
        await asyncio.sleep(60)
