"""Health monitor for OpenClaw gateway — pure subprocess, NO LLM cost.

Runs in a background thread, checking every 5 minutes:
  - systemd service status
  - Disk space (alert if >90%)
  - Memory usage (alert if >90%)
  - OpenClaw health endpoint (if gateway is active)

Auto-restarts the gateway on failure (with cooldown and hourly limit).
Sends Telegram alerts via direct HTTP POST (no python-telegram-bot needed).
"""

from __future__ import annotations

import logging
import subprocess
import threading
import time
from typing import Optional

import httpx

logger = logging.getLogger(__name__)

# Defaults
DEFAULT_CHECK_INTERVAL = 300  # 5 minutes
RESTART_COOLDOWN = 600        # 10 minutes between auto-restarts
MAX_RESTARTS_PER_HOUR = 3
SERVICE_NAME = "openclaw-gateway"


class HealthMonitor:
    """Background health checker for OpenClaw gateway."""

    def __init__(
        self,
        telegram_token: str = "",
        telegram_chat_id: int = 0,
        check_interval: int = DEFAULT_CHECK_INTERVAL,
        auto_restart: bool = True,
        max_restarts: int = MAX_RESTARTS_PER_HOUR,
    ) -> None:
        self._telegram_token = telegram_token
        self._telegram_chat_id = telegram_chat_id
        self._check_interval = check_interval
        self._auto_restart = auto_restart
        self._max_restarts = max_restarts

        self._stop = threading.Event()
        self._thread: Optional[threading.Thread] = None
        self._last_restart: float = 0
        self._restart_count: int = 0
        self._restart_hour_start: float = 0
        self._http = httpx.Client(timeout=10)

    def start(self) -> None:
        """Start the health monitor in a background daemon thread."""
        self._thread = threading.Thread(
            target=self._run_loop,
            name="easyclaw-health-monitor",
            daemon=True,
        )
        self._thread.start()
        logger.info("Health monitor started (interval=%ds)", self._check_interval)

    def stop(self) -> None:
        """Signal the monitor to stop."""
        self._stop.set()
        if self._thread:
            self._thread.join(timeout=10)
        self._http.close()
        logger.info("Health monitor stopped")

    # ------------------------------------------------------------------
    # Main loop
    # ------------------------------------------------------------------

    def _run_loop(self) -> None:
        """Check health every interval until stopped."""
        self._stop.wait(30)  # Initial delay — let services settle

        while not self._stop.is_set():
            try:
                self._check_health()
            except Exception:
                logger.exception("Health check error")
            self._stop.wait(self._check_interval)

    def _check_health(self) -> None:
        """Run all health checks."""
        service_active = self._check_service()

        if not service_active and self._auto_restart:
            self._handle_service_down()
            return

        disk_pct = self._check_disk()
        if disk_pct > 90:
            self._send_alert(
                f"\u26a0\ufe0f Disk usage at {disk_pct}% \u2014 consider freeing space."
            )

        mem_pct = self._check_memory()
        if mem_pct > 90:
            self._send_alert(
                f"\u26a0\ufe0f Memory usage at {mem_pct}% \u2014 consider restarting services."
            )

        if service_active:
            health_ok = self._check_openclaw_health()
            if not health_ok:
                self._send_alert(
                    "\u26a0\ufe0f OpenClaw gateway is running but health check failed. "
                    "Run /health for details."
                )

    # ------------------------------------------------------------------
    # Individual checks (all subprocess-based, no LLM)
    # ------------------------------------------------------------------

    def _check_service(self) -> bool:
        """Check if openclaw-gateway systemd service is active."""
        try:
            result = subprocess.run(
                ["systemctl", "is-active", SERVICE_NAME],
                capture_output=True, text=True, timeout=10,
            )
            return result.stdout.strip() == "active"
        except Exception:
            return False

    def _check_disk(self) -> int:
        """Return disk usage percentage for /."""
        try:
            result = subprocess.run(
                ["df", "--output=pcent", "/"],
                capture_output=True, text=True, timeout=10,
            )
            lines = result.stdout.strip().split("\n")
            if len(lines) >= 2:
                return int(lines[1].strip().rstrip("%"))
        except Exception:
            pass
        return 0

    def _check_memory(self) -> int:
        """Return memory usage percentage."""
        try:
            result = subprocess.run(
                ["free", "-m"],
                capture_output=True, text=True, timeout=10,
            )
            for line in result.stdout.split("\n"):
                if line.startswith("Mem:"):
                    parts = line.split()
                    total = int(parts[1])
                    used = int(parts[2])
                    if total > 0:
                        return int(used / total * 100)
        except Exception:
            pass
        return 0

    def _check_openclaw_health(self) -> bool:
        """Run openclaw health command."""
        try:
            result = subprocess.run(
                ["openclaw", "health"],
                capture_output=True, text=True, timeout=30,
            )
            return result.returncode == 0
        except Exception:
            return False

    # ------------------------------------------------------------------
    # Auto-restart logic
    # ------------------------------------------------------------------

    def _handle_service_down(self) -> None:
        """Attempt to auto-restart the gateway."""
        now = time.time()

        # Reset hourly counter
        if now - self._restart_hour_start > 3600:
            self._restart_count = 0
            self._restart_hour_start = now

        if self._restart_count >= self._max_restarts:
            self._send_alert(
                f"\u274c OpenClaw gateway is DOWN. "
                f"Auto-restart limit reached ({self._max_restarts}/hour). "
                "Manual intervention required."
            )
            return

        if now - self._last_restart < RESTART_COOLDOWN:
            return  # Too soon

        logger.info("OpenClaw gateway is down, attempting auto-restart...")
        try:
            subprocess.run(
                ["systemctl", "restart", SERVICE_NAME],
                capture_output=True, text=True, timeout=30,
            )
        except Exception as exc:
            self._send_alert(f"\u274c Failed to restart OpenClaw: {exc}")
            return

        self._last_restart = now
        self._restart_count += 1

        time.sleep(8)
        if self._check_service():
            self._send_alert(
                "\u2705 OpenClaw gateway was down \u2014 auto-restarted successfully."
            )
        else:
            self._send_alert(
                "\u274c OpenClaw gateway auto-restart FAILED. "
                "Service is still down. Manual intervention required."
            )

    # ------------------------------------------------------------------
    # Telegram alerts (direct HTTP, no library needed)
    # ------------------------------------------------------------------

    def _send_alert(self, message: str) -> None:
        """Send a Telegram alert message."""
        logger.warning("Health alert: %s", message)

        if not self._telegram_token or not self._telegram_chat_id:
            return

        try:
            url = f"https://api.telegram.org/bot{self._telegram_token}/sendMessage"
            self._http.post(url, json={
                "chat_id": self._telegram_chat_id,
                "text": message,
                "parse_mode": "HTML",
            })
        except Exception:
            logger.exception("Failed to send Telegram alert")

    # ------------------------------------------------------------------
    # Public status (for HTTP API / Telegram commands)
    # ------------------------------------------------------------------

    def get_status(self) -> dict:
        """Return current health status as a dict."""
        return {
            "service_active": self._check_service(),
            "disk_percent": self._check_disk(),
            "memory_percent": self._check_memory(),
            "auto_restart_enabled": self._auto_restart,
            "restarts_this_hour": self._restart_count,
            "max_restarts_per_hour": self._max_restarts,
        }
