"""
MC2-Steward — die Wächter-Loops als EIGENER Prozess (UMBAU v3, P2).

Bisher hingen Re-Warm-Wächter, Health-Wächter (sentry) und Mem0-Auto-Dedupe am
Lebenszyklus des Steuerpult-Webservers (app.py-Lifespan): jeder MC2-Neustart riss
den Wächtern Timing und Flanken-Gedächtnis weg — und ein TOTES Steuerpult konnte
sich prinzipbedingt nicht selbst melden. Hier laufen DIESELBEN Loops (unveränderte
Module) als eigener Mini-Dienst (mc2-steward.service, Restart=always):

  • warmer.rewarm_loop      — Warm-Set nachladen (+ Config-Watch ersetzt den
                              In-Process-Nudge aus llamaswap.write_config)
  • sentry.sentry_loop      — Health-Flanken → Briefkasten (HTTP an MC2) + Telegram;
                              beobachtet im Steward-Modus AUCH MC2 selbst + mc2-gateway
  • memory.auto_dedupe_loop — Gedächtnis-Dubletten (HTTP an den Mem0-Sidecar)

BEWUSST NICHT hier: reminders_loop — der teilt sich Datei UND CRUD-Pfade mit dem
/api/reminders-Router (Zwei-Schreiber-Risiko auf mc2-reminders.json); er bleibt im
Steuerpult. Der Briefkasten-Store gehört weiter EXKLUSIV dem MC2-Prozess — dieser
Prozess liefert Meldungen per HTTP ab (announce.py, MC_ANNOUNCE_HTTP).

Die Loop-Schalter (MC_REWARM_ENABLED / MC_SENTRY_ENABLED / MC_MEM_DEDUPE_ENABLED)
stehen in der MC2-Unit auf 0 und hier auf Default 1 — reiner Konfig-Split, kein
Verhaltens-Code im Steuerpult angefasst. Zeilen dort entfernen = Rollback.
"""

import asyncio
import logging
import os

from config import CONFIG_PATH
from services import memory as memory_svc
from services import sentry, warmer

logging.basicConfig(
    level=os.environ.get("MC_LOG_LEVEL", "INFO").upper(),
    format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
)
log = logging.getLogger("steward")

# Wie oft nach einer llama-swap-Config-Änderung geschaut wird (mtime-Watch). Ersetzt
# warmer.nudge() aus dem MC2-Prozess: ein -watch-config-Reload verwirft das Warm-Set,
# wir sehen die Änderung binnen Sekunden statt erst beim nächsten 90s-Tick.
CONFIG_WATCH_S = int(os.environ.get("MC_STEWARD_CONFIG_WATCH_S", "5"))


async def config_watch() -> None:
    last: float | None = None
    while True:
        try:
            mtime = CONFIG_PATH.stat().st_mtime
        except OSError:
            mtime = None
        if last is not None and mtime is not None and mtime != last:
            log.info("steward: %s geändert → Warm-Set-Nudge", CONFIG_PATH)
            warmer.nudge()
        last = mtime
        await asyncio.sleep(CONFIG_WATCH_S)


async def main() -> None:
    tasks: list[asyncio.Task] = []
    if warmer.ENABLED:
        tasks.append(asyncio.create_task(warmer.rewarm_loop()))
        tasks.append(asyncio.create_task(config_watch()))
        log.info("Re-Warm-Wächter aktiv (Intervall %ss, Config-Watch %ss)",
                 warmer.INTERVAL, CONFIG_WATCH_S)
    if sentry.ENABLED:
        tasks.append(asyncio.create_task(sentry.sentry_loop()))
    if memory_svc.AUTO_DEDUPE_ENABLED:
        tasks.append(asyncio.create_task(memory_svc.auto_dedupe_loop()))
        log.info("Mem0-Auto-Dedupe aktiv (alle %ss, Schwelle %s)",
                 memory_svc.AUTO_DEDUPE_INTERVAL, memory_svc.AUTO_DEDUPE_THRESHOLD)
    if not tasks:
        log.warning("steward: alle Loops per Env deaktiviert — nichts zu tun, Ende.")
        return
    await asyncio.gather(*tasks)


if __name__ == "__main__":
    asyncio.run(main())
