#!/usr/bin/env python3
# Notifiq Webhook

import os
import json
import sys
import ssl
import urllib.request
from urllib.parse import urlparse

TTL = 15000  # ms

def system_ca_bundle():
    for p in (
        "/etc/ssl/certs/ca-certificates.crt",   # Debian/Ubuntu
        "/etc/pki/tls/certs/ca-bundle.crt",     # RHEL-like
        "/etc/ssl/ca-bundle.pem",
    ):
        if os.path.exists(p):
            return p
    return None

def get_env(name: str, default: str = "") -> str:
    return os.environ.get(name, default)


def get_api_url() -> str:
    """
    NOTIFY_PARAMETER_1 is required.

    Accepts:
      - https://fqdn or http://fqdn (optionally :PORT) -> https?://fqdn(:PORT)/api/notify
      - fqdn, fqdn:PORT, IP, IP:PORT                  -> http://<arg>/api/notify

    Always normalizes the path to /api/notify.
    """
    arg = os.environ.get("NOTIFY_PARAMETER_1", "").strip()

    if not arg:
        raise SystemExit("Missing Notifiq URL in NOTIFY_PARAMETER_1 (Checkmk notification parameter 1)")

    # If scheme is missing, assume http://
    if "://" not in arg:
        arg = f"http://{arg}"

    parsed = urlparse(arg)

    scheme = parsed.scheme or "http"
    netloc = parsed.netloc

    # Safety for weird inputs where netloc ends up empty
    if not netloc and parsed.path:
        netloc = parsed.path.split("/")[0]

    return f"{scheme}://{netloc}/api/notify"

def get_api_token() -> str:
    # Put the token in Checkmk notification parameter 2
    return os.environ.get("NOTIFY_PARAMETER_2", "").strip()

def build_payload() -> dict:
    what = get_env("NOTIFY_WHAT")  # "HOST" or "SERVICE"
    hostname = get_env("NOTIFY_HOSTNAME")
    servicedesc = get_env("NOTIFY_SERVICEDESC")

    service_state_raw = get_env("NOTIFY_SERVICESTATE")
    host_state_raw = get_env("NOTIFY_HOSTSTATE")

    notif_type_raw = get_env("NOTIFY_NOTIFICATIONTYPE")
    notif_type = (notif_type_raw or "").strip().upper()

    service_output = get_env("NOTIFY_SERVICEOUTPUT")
    host_output = get_env("NOTIFY_HOSTOUTPUT")

    # Pick the relevant state + normalize
    state_raw = service_state_raw if what == "SERVICE" else host_state_raw
    state = (state_raw or "").strip().upper()

    # Map state -> base level (robust for short/long variants)
    def level_from_state(s: str) -> str:
        if s.startswith(("CRIT", "CRITICAL", "DOWN", "UNKNOWN", "UNREACH", "UNREACHABLE")):
            return "critical"
        if s.startswith(("WARN", "WARNING")):
            return "warning"
        if s.startswith(("OK", "UP")):
            return "success"
        return "info"

    level = level_from_state(state)

    # Notification type -> prefix + (optional) level override
    type_rules = [
        (("PROBLEM",),           "PROBLEM",             None),
        (("RECOVERY",),          "RECOVERY",            "success"),
        (("ACKNOWLEDGEMENT",),   "ACK",                 "info"),
        (("FLAPPINGSTART",),     "FLAPPING START",      "warning"),
        (("FLAPPINGSTOP",),      "FLAPPING STOP",       "info"),
        (("DOWNTIMESTART",),     "DOWNTIME START",      "info"),
        (("DOWNTIMEEND",),       "DOWNTIME END",        "info"),
        (("DOWNTIMECANCELLED",), "DOWNTIME CANCELLED",  "warning"),
        (("CUSTOM",),            "CUSTOM",              "info"),
        (("ALERTHANDLER",),      "ALERTHANDLER",        "info"),
    ]

    prefix = "NOTIFY"
    for keys, pfx, override_level in type_rules:
        if notif_type.startswith(keys):
            prefix = pfx
            if override_level:
                level = override_level
            break

    # Title + message
    if what == "SERVICE":
        base_title = f"Service {servicedesc} on {hostname}"
        message = (
            f"Type: {notif_type_raw}\n"
            f"Host: {hostname}\n"
            f"Service: {servicedesc}\n"
            f"State: {state_raw}\n"
            f"Summary: {service_output}"
        )
    else:
        base_title = f"Host {hostname}"
        message = (
            f"Type: {notif_type_raw}\n"
            f"Host: {hostname}\n"
            f"State: {state_raw}\n"
            f"Summary: {host_output}"
        )

    title = f"{prefix}: Checkmk {base_title}"

    return {
        "title": title,
        "level": level,
        "ttl": TTL,
        "message": message,
    }

def send_notification(payload: dict) -> int:
    api_url = get_api_url()
    data = json.dumps(payload).encode("utf-8")

    headers = {"Content-Type": "application/json"}
    token = get_api_token()
    if not token:
        raise SystemExit("Missing API token in NOTIFY_PARAMETER_2 (Checkmk notification parameter 2)")
    headers["Authorization"] = f"Bearer {token}"

    req = urllib.request.Request(
        api_url,
        data=data,
        headers=headers,
        method="POST",
    )

    try:
        # Only needed for HTTPS
        if api_url.startswith("https://"):
            cafile = system_ca_bundle()
            ctx = ssl.create_default_context(cafile=cafile) if cafile else ssl.create_default_context()
            with urllib.request.urlopen(req, timeout=5, context=ctx) as resp:
                resp.read()
        else:
            with urllib.request.urlopen(req, timeout=5) as resp:
                resp.read()

        return 0
    except Exception as e:
        print(f"Error sending notification: {e}", file=sys.stderr)
        return 1

def main() -> int:
    payload = build_payload()
    return send_notification(payload)


if __name__ == "__main__":
    sys.exit(main())
