"""
monitor_service.py — surveillance périodique (worker leader) : alerte par e-mail
si l'organizer FTP est bloqué ou en erreur répétée, si le disque est presque
plein ou si la base ne répond plus.
"""
import asyncio
import logging
import shutil
import time

from core import config
from core.db import get_db
from services import alert_service

log = logging.getLogger("meeconic.monitor")

_INTERVAL = 300  # 5 min
_MIN_FREE_BYTES = 1 * 1024 ** 3  # 1 Go
_STARTED = time.monotonic()
_GRACE_SECS = 120   # los organizers tardan un momento en arrancar: no alertar en el arranque


def check_once() -> list[str]:
    problems: list[str] = []
    try:
        with get_db() as conn:
            conn.execute("SELECT 1").fetchone()
    except Exception as e:
        problems.append(f"Base de données inaccessible : {e}")
        alert_service.notify("db-down", "Base de données inaccessible", str(e))

    try:
        free = shutil.disk_usage(config.PHOTOS_DIR if config.PHOTOS_DIR.exists() else config.BACKUPS_DIR.parent).free
        if free < _MIN_FREE_BYTES:
            msg = f"Espace disque faible : {free // (1024 * 1024)} Mo libres."
            problems.append(msg)
            alert_service.notify("disk-low", "Espace disque faible", msg)
    except Exception as e:
        log.warning("Disk check failed: %s", e)

    if config.ORGANIZER_ENABLED and time.monotonic() - _STARTED > _GRACE_SECS:
        try:
            from services import school_service
            from services.organizer_service import get_organizer_status
            for school in school_service.list_schools(include_inactive=False):
                sid = school["id"]
                st = get_organizer_status(sid)
                if st.get("stalled"):
                    why = "su bucle no está vivo" if not st.get("alive") else "hay fotos esperando y no se mueve nada"
                    msg = f"[{sid}] El organizer está atascado: {why} ({st.get('pending_count', 0)} pendientes)."
                    problems.append(msg)
                    alert_service.notify(f"organizer-stalled:{sid}", f"Ingestión FTP bloqueada ({sid})", msg)
                if (st.get("consecutive_errors") or 0) >= 5:
                    msg = f"[{sid}] {st['consecutive_errors']} errores seguidos. Último: {st.get('last_error')}"
                    problems.append(msg)
                    alert_service.notify(f"organizer-errors:{sid}", f"Ingestión FTP con errores repetidos ({sid})", msg)
        except Exception as e:
            log.warning("Organizer check failed: %s", e)
    return problems


async def run_monitor_loop() -> None:
    while True:
        try:
            await asyncio.to_thread(check_once)
        except asyncio.CancelledError:
            raise
        except Exception as e:
            log.error("Erreur du moniteur: %s", e, exc_info=True)
        await asyncio.sleep(_INTERVAL)
