#!/usr/bin/env python3
import json, os, signal, sys, time, threading
from datetime import datetime, timezone

from meshtastic.serial_interface import SerialInterface
from meshtastic.mesh_interface import MeshInterface
from pubsub import pub

PORT = os.environ.get("MESH_SERIAL", "/dev/ttyACM0")
OUT = os.environ.get("MESH_DUMP_FILE", "meshtastic-dump.ndjson")

# line-buffered append so you can tail -f safely
f = open(OUT, "a", buffering=1, encoding="utf-8")


def now():
    return datetime.now(timezone.utc).isoformat()


def write(kind, payload):
    rec = {"ts": now(), "kind": kind, **payload}
    f.write(json.dumps(rec, ensure_ascii=False, default=str) + "\n")


# Connect to the node
iface: MeshInterface = SerialInterface(PORT)


# Packet callback: every RF/Mesh packet the node receives/decodes lands here
def on_packet(packet, iface):
    # 'packet' already includes decoded fields when available (portnum, payload, position, telemetry, etc.)
    write("packet", {"packet": packet})


# Node callback: topology/metadata updates (nodeinfo, hops, lastHeard, etc.)
def on_node(node, iface):
    write("node", {"node": node})


iface.onReceive = on_packet
pub.subscribe(on_node, "meshtastic.node")

# Write a little header so you know what you captured
try:
    my = getattr(iface, "myInfo", None)
    write(
        "meta",
        {
            "event": "started",
            "port": PORT,
            "my_node_num": getattr(my, "my_node_num", None) if my else None,
        },
    )
except Exception as e:
    write("meta", {"event": "started", "port": PORT, "error": str(e)})


# Keep the process alive until Ctrl-C
def _stop(signum, frame):
    write("meta", {"event": "stopping"})
    try:
        try:
            pub.unsubscribe(on_node, "meshtastic.node")
        except Exception:
            pass
        iface.close()
    finally:
        f.close()
    sys.exit(0)


signal.signal(signal.SIGINT, _stop)
signal.signal(signal.SIGTERM, _stop)

# Simple sleep loop; avoids busy-wait
while True:
    time.sleep(1)
