#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 12-load — 42i-hc Drop-in: volatile Host-Last (RAM, CPU/Kerne, Load, Uptime).
# hc-tags: cheap, metrics, local, host
# hc-ttl: 5
#
# Liefert {"metrics": {...}}. Volatile Werte, daher kurze Cache-TTL (5 s). RAM
# bevorzugt die cgroup-v2-Sicht (memory.current/max) und faellt sonst auf
# /proc/meminfo zurueck. Schwellen aus HC_WARN_PCT/HC_FAIL_PCT (Default 85/95).
#
# Geliefert von 42i-hc.

import json
import os
import sys

WARN = float(os.environ.get("HC_WARN_PCT") or 85)
FAIL = float(os.environ.get("HC_FAIL_PCT") or 95)
CGROUP = "/sys/fs/cgroup"


def classify(pct):
    """Bildet einen Prozentwert auf ok/warn/fail nach den Schwellen ab."""
    if pct >= FAIL:
        return "fail"
    if pct >= WARN:
        return "warn"
    return "ok"


def read_int(path):
    """Liest eine einzelne Ganzzahl aus einer Datei (None bei Fehler/'max')."""
    try:
        with open(path, "r") as fh:
            val = fh.read().strip()
        return None if val in ("", "max") else int(val)
    except Exception:
        return None


def mem():
    """RAM-Auslastung: cgroup-v2-Limit bevorzugt, sonst /proc/meminfo."""
    used = read_int(os.path.join(CGROUP, "memory.current"))
    limit = read_int(os.path.join(CGROUP, "memory.max"))
    if used is not None and limit:
        pct = round(used / limit * 100, 1)
        return {"used_mb": used // 1048576, "limit_mb": limit // 1048576,
                "pct": pct, "status": classify(pct)}
    try:  # Fallback /proc/meminfo (Linux ohne cgroup-Limit)
        info = {}
        with open("/proc/meminfo") as fh:
            for line in fh:
                k, v = line.split(":", 1)
                info[k] = int(v.strip().split()[0])  # kB
        total = info.get("MemTotal", 0)
        avail = info.get("MemAvailable", info.get("MemFree", 0))
        if total:
            pct = round((total - avail) / total * 100, 1)
            return {"used_mb": (total - avail) // 1024, "limit_mb": total // 1024,
                    "pct": pct, "status": classify(pct)}
    except Exception:
        pass
    return None


def cpu_cores():
    """Zugewiesene Kerne: cgroup cpu.max-Quota bevorzugt, sonst os.cpu_count()."""
    try:
        with open(os.path.join(CGROUP, "cpu.max")) as fh:
            quota, period = fh.read().split()
        if quota != "max":
            return round(int(quota) / int(period), 2)
    except Exception:
        pass
    return os.cpu_count()


def main():
    """Sammelt die volatilen Metriken defensiv und gibt {"metrics": ...} aus."""
    metrics = {}
    try:
        metrics["load"] = [round(x, 2) for x in os.getloadavg()]
    except Exception:
        pass
    cores = cpu_cores()
    if cores:
        metrics["cores"] = cores
    try:
        with open("/proc/uptime") as fh:
            metrics["uptime_s"] = int(float(fh.read().split()[0]))
    except Exception:
        pass
    m = mem()
    if m:
        metrics["mem"] = m
    json.dump({"metrics": metrics}, sys.stdout, ensure_ascii=False)
    sys.stdout.write("\n")


if __name__ == "__main__":
    main()
