# retoor <retoor@molodetz.nl>

from __future__ import annotations

import asyncio
import sys

from devplacepy.config import XMLRPC_BIND, XMLRPC_PORT
from devplacepy.services.base import BaseService

XMLRPC_INTERVAL_SECONDS = 15
SERVER_MODULE = "devplacepy.services.xmlrpc.server"
TERMINATE_TIMEOUT_SECONDS = 10


class XmlrpcService(BaseService):
    title = "XML-RPC Bridge"
    description = (
        "Forking XML-RPC server that exposes every documented REST endpoint as an "
        "XML-RPC method. Reachable at /xmlrpc through the app and nginx."
    )
    default_enabled = True
    min_interval = 5

    def __init__(self) -> None:
        super().__init__("xmlrpc", interval_seconds=XMLRPC_INTERVAL_SECONDS)
        self._process: asyncio.subprocess.Process | None = None

    def _alive(self) -> bool:
        return self._process is not None and self._process.returncode is None

    async def _spawn(self) -> None:
        self._process = await asyncio.create_subprocess_exec(
            sys.executable,
            "-m",
            SERVER_MODULE,
            stdout=asyncio.subprocess.DEVNULL,
            stderr=asyncio.subprocess.DEVNULL,
        )
        self.log(
            f"Forking XML-RPC server started (pid {self._process.pid}) on "
            f"{XMLRPC_BIND}:{XMLRPC_PORT}"
        )

    async def _terminate(self) -> None:
        process = self._process
        self._process = None
        if process is None or process.returncode is not None:
            return
        process.terminate()
        try:
            await asyncio.wait_for(process.wait(), timeout=TERMINATE_TIMEOUT_SECONDS)
        except asyncio.TimeoutError:
            process.kill()
            await process.wait()
        self.log("Forking XML-RPC server stopped")

    async def on_enable(self) -> None:
        if not self._alive():
            await self._spawn()

    async def on_disable(self) -> None:
        await self._terminate()

    async def run_once(self) -> None:
        if self._alive():
            self.log(f"XML-RPC server healthy (pid {self._process.pid})")
            return
        self.log("XML-RPC server not running, starting it")
        await self._spawn()

    def collect_metrics(self) -> dict:
        return {
            "running": self._alive(),
            "pid": self._process.pid if self._alive() else None,
            "bind": f"{XMLRPC_BIND}:{XMLRPC_PORT}",
        }


__all__ = ["XmlrpcService"]
