#!/usr/bin/env python3
"""
CNPERP on-premise license heartbeat — register local node and ping cloud hub.

Copy deploy/license-node.example.json to deploy/license-node.json and edit.
Schedule every 5 minutes (cron / Task Scheduler).

  python scripts/heartbeat_license.py
  python scripts/heartbeat_license.py --config deploy/license-node.json
"""
from __future__ import annotations

import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
DEFAULT_CONFIG = ROOT / "deploy" / "license-node.json"


def load_config(path: Path) -> dict:
    if not path.is_file():
        print(f"Config not found: {path}")
        print("Copy deploy/license-node.example.json to deploy/license-node.json")
        sys.exit(1)
    with path.open(encoding="utf-8") as f:
        return json.load(f)


def save_config(path: Path, cfg: dict) -> None:
    with path.open("w", encoding="utf-8") as f:
        json.dump(cfg, f, indent=2)
        f.write("\n")


def post_json(url: str, payload: dict) -> dict:
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url,
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=60) as resp:
        return json.loads(resp.read().decode("utf-8"))


def main() -> None:
    parser = argparse.ArgumentParser(description="CNPERP license node heartbeat")
    parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG)
    args = parser.parse_args()

    cfg = load_config(args.config)
    base = (cfg.get("cloud_url") or os.environ.get("LICENSE_CLOUD_URL") or "").rstrip("/")
    if not base:
        print("Set cloud_url in config or LICENSE_CLOUD_URL env var")
        sys.exit(1)

    license_key = (cfg.get("license_key") or "").strip()
    if not license_key:
        print("license_key is required in config")
        sys.exit(1)

    node_id = (cfg.get("node_id") or "").strip()
    sync_token = (cfg.get("sync_token") or "").strip()

    try:
        if not node_id or not sync_token:
            print(f"Registering node with {base} …")
            result = post_json(
                f"{base}/api/v1/licensing/nodes/register",
                {
                    "license_key": license_key,
                    "node_name": cfg.get("node_name") or "CNPERP Local",
                    "public_base_url": cfg.get("public_base_url"),
                    "machine_fingerprint": cfg.get("machine_fingerprint") or None,
                    "app_version": cfg.get("app_version"),
                },
            )
            cfg["node_id"] = result["node_id"]
            cfg["sync_token"] = result["sync_token"]
            save_config(args.config, cfg)
            print(f"Registered node_id={result['node_id']}")
            print("Sync token saved to config — keep this file secure.")
            return

        print(f"Heartbeat → {base} (node {node_id}) …")
        result = post_json(
            f"{base}/api/v1/licensing/nodes/heartbeat",
            {
                "node_id": node_id,
                "sync_token": sync_token,
                "app_version": cfg.get("app_version"),
            },
        )
        print(result.get("message", "OK"))
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")
        print(f"HTTP {e.code}: {body}")
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
