#!/usr/bin/env python3
"""kuma-add-ping.py — add ping monitors to Uptime Kuma by name [#435][#705]

Companion to kuma-inventory.py (same connect pattern). Reads a list of
hostnames from argv or a file (one per line), skips any that already have
a ping monitor with the same name or target, and adds the rest with the
fleet-standard ping profile. Print-only summary; never deletes.

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

Usage:
  python3 kuma-add-ping.py host1.knel.net host2.knel.net
  python3 kuma-add-ping.py --file hosts.txt
"""
import argparse
import os
import time

import socketio

INTERVAL = 60  # fleet-standard ping cadence (seconds)


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("hosts", nargs="*")
    ap.add_argument("--file", help="file with one hostname per line")
    args = ap.parse_args()

    hosts = list(args.hosts)
    if args.file:
        with open(args.file) as fh:
            hosts += [ln.strip() for ln in fh if ln.strip()]
    if not hosts:
        raise SystemExit("no hosts given")

    sio, mons = kuma_connect()
    existing_names = {(m.get("name") or "").lower() for m in mons.values()}
    existing_targets = {(m.get("hostname") or m.get("url") or "").lower()
                        for m in mons.values()}

    added = skipped = 0
    for host in hosts:
        if host.lower() in existing_names or host.lower() in existing_targets:
            print(f"  = {host} (monitor exists)")
            skipped += 1
            continue
        res = sio.call("add", {
            "type": "ping",
            "name": host,
            "hostname": host,
            "interval": INTERVAL,
            "retryInterval": 60,
            "maxretries": 2,
            "notificationIDList": {},
        }, timeout=30)
        if isinstance(res, dict) and res.get("ok"):
            print(f"  + {host}")
            added += 1
        else:
            print(f"  ! {host}: {res}")

    print(f"done: +{added} added, ={skipped} skipped, "
          f"{len(hosts) - added - skipped} failed")
    sio.disconnect()


if __name__ == "__main__":
    main()
