#!/usr/bin/env python3
"""kuma-inventory.py — read-only Uptime Kuma monitor inventory dump [#435]

Connects via the socket.io API (same pattern as kuma-fleet-sync.py) and
prints every monitor with id, type, parent (group), name, target, active
state. Read-only: emits nothing back to the server. Used to plan group
placement and coverage gaps; never mutates.

Env (from ~/.creds/uptime-kuma.env):
  UPTIME_KUMA_URL, UPTIME_KUMA_TOKEN

Usage:
  python3 kuma-inventory.py            # TSV dump: id<TAB>parent<TAB>type<TAB>active<TAB>name<TAB>target
  python3 kuma-inventory.py --summary  # group counts + root-level offenders only
"""
import argparse
import os
import time

import socketio


def kuma_connect():
    sio = socketio.Client(reconnection=False)
    state = {}

    @sio.on("monitorList")
    def _ml(d):
        state["ml"] = d

    sio.connect(
        os.environ["UPTIME_KUMA_URL"], transports=["websocket"], wait_timeout=30
    )
    r = sio.call("loginByToken", os.environ["UPTIME_KUMA_TOKEN"], timeout=30)
    if not (isinstance(r, dict) and r.get("ok")):
        raise SystemExit(f"login failed: {r}")
    sio.emit("monitorList")
    for _ in range(20):
        if "ml" in state:
            break
        time.sleep(1)
    return sio, state.get("ml", {})


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--summary", action="store_true")
    args = ap.parse_args()

    sio, mons = kuma_connect()
    try:
        by_id = {str(m.get("id")): m for m in mons.values()}

        def name_of(mid):
            m = by_id.get(str(mid))
            return (m.get("name") or "?") if m else f"id{mid}"

        rows = []
        for m in mons.values():
            mid = m.get("id")
            parent = m.get("parent") or m.get("parent_id") or ""
            parent = name_of(parent) if parent not in ("", None) else "-"
            mtype = m.get("type") or "?"
            active = "on" if m.get("active", True) else "PAUSED"
            name = (m.get("name") or "?").replace("\t", " ")
            # hostname first: ping monitors carry a junk default url ("https://")
            target = m.get("hostname") or m.get("url") or ""
            target = str(target).replace("\t", " ")
            rows.append((mid, parent, mtype, active, name, target))
        rows.sort(key=lambda r: (r[1] == "-", r[1], r[4].lower()))

        if args.summary:
            groups = [r for r in rows if r[2] == "group"]
            root = [r for r in rows if r[1] == "-" and r[2] != "group"]
            print(f"total monitors: {len(rows)}  groups: {len(groups)}  root-level: {len(root)}")
            print("\ngroups:")
            for r in groups:
                kids = sum(1 for x in rows if x[1] == r[4])
                print(f"  {r[4]}  ({kids} children, id={r[0]})")
            print("\nroot-level monitors (must move into groups):")
            for r in root:
                print(f"  id={r[0]}  [{r[2]}]  {r[3]}  {r[4]}  {r[5]}")
        else:
            for r in rows:
                print("\t".join(str(c) for c in r))
    finally:
        sio.disconnect()


if __name__ == "__main__":
    main()
