#!/usr/bin/env python3
"""NotifyBell CLI — send notifications from the command line.

Usage:
  nb notify --title "Server Down" --priority urgent
  nb notify --title "Deploy" --body "v1.2 shipped" --team engineering
  nb heartbeat my-cron-job
  echo "alert text" | nb notify --title "From stdin"

Configure via env vars:
  NOTIFYBELL_API_KEY  (required)
  NOTIFYBELL_URL      (default: https://app.notifybell.com/api/v1)
"""

import argparse
import json
import os
import sys
import urllib.request
import urllib.error


def _api(method: str, path: str, data: dict | None = None) -> dict:
    url = os.environ.get("NOTIFYBELL_URL", "https://app.notifybell.com/api/v1").rstrip("/") + path
    api_key = os.environ.get("NOTIFYBELL_API_KEY")
    if not api_key:
        print("ERROR: NOTIFYBELL_API_KEY env var not set", file=sys.stderr)
        sys.exit(2)
    headers = {"X-API-Key": api_key, "Content-Type": "application/json"}
    body = json.dumps(data).encode() if data else None
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            return json.loads(resp.read())
    except urllib.error.HTTPError as e:
        print(f"ERROR {e.code}: {e.read().decode()}", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)


def cmd_notify(args):
    # If no title from flags and stdin has data, use stdin as body
    body = args.body
    if not body and not sys.stdin.isatty():
        body = sys.stdin.read().strip()

    payload = {
        "title": args.title,
        "priority": args.priority,
        "category": args.category,
        "sound": args.sound,
        "fullscreen": args.fullscreen,
        "require_ack": args.ack,
    }
    if body:
        payload["body"] = body
    if args.channel:
        payload["channel"] = args.channel
    if args.user:
        payload["user"] = args.user
    if args.team:
        payload["team"] = args.team
    if args.url:
        payload["url"] = args.url

    result = _api("POST", "/notify", payload)
    print(json.dumps(result, indent=2))


def cmd_heartbeat(args):
    # Authenticated by the heartbeat's ping token, not its slug: the slug is a
    # label anyone can guess, and a guessable ping lets a dead job look alive.
    result = _api("GET", f"/heartbeats/ping/{args.token}")
    print(json.dumps(result, indent=2))


def cmd_test(args):
    """Test connectivity to the NotifyBell API."""
    result = _api("GET", "/health")
    print(f"✓ NotifyBell API reachable: {result}")


def main():
    parser = argparse.ArgumentParser(prog="nb", description="NotifyBell CLI")
    sub = parser.add_subparsers(dest="command", required=True)

    p_notify = sub.add_parser("notify", help="Send a notification")
    p_notify.add_argument("--title", required=True)
    p_notify.add_argument("--body", default=None, help="Notification body (or pipe via stdin)")
    p_notify.add_argument("--priority", default="normal",
                          choices=["silent", "low", "normal", "high", "urgent", "critical", "emergency"])
    p_notify.add_argument("--category", default="info")
    p_notify.add_argument("--sound", default="default")
    p_notify.add_argument("--channel", default=None)
    p_notify.add_argument("--user", default=None)
    p_notify.add_argument("--team", default=None)
    p_notify.add_argument("--url", default=None)
    p_notify.add_argument("--fullscreen", action="store_true")
    p_notify.add_argument("--ack", action="store_true", help="Require acknowledgment")
    p_notify.set_defaults(func=cmd_notify)

    p_hb = sub.add_parser("heartbeat", help="Send a heartbeat ping")
    p_hb.add_argument("token", help="Heartbeat ping token (from the heartbeat's ping URL)")
    p_hb.set_defaults(func=cmd_heartbeat)

    p_test = sub.add_parser("test", help="Test API connectivity")
    p_test.set_defaults(func=cmd_test)

    args = parser.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
