"""Auto-reply to received text messages.

Purpose: demonstrate receive callback + generated reply flow.
Transport scope: Serial default, optional TCP/BLE.
Behavior: listens for text, prints message metadata, sends one reply per text message.
Expected output: "Connected..." plus message/reply lines while running.
Cleanup/error handling: clear connect failures and graceful Ctrl+C close.
"""

import argparse
import time
from typing import Any, Optional, Union
from pubsub import pub
import meshtastic.serial_interface
import meshtastic.tcp_interface
import meshtastic.ble_interface
from meshtastic.mesh_interface import MeshInterface

def onReceive(packet: dict, interface: MeshInterface) -> None:
    """Reply to every received packet with some info"""
    text: Optional[str] = packet.get("decoded", {}).get("text")
    if text:
        rx_snr: Any = packet.get("rxSnr", "unknown")
        hop_limit: Any = packet.get("hopLimit", "unknown")
        print(f"message: {text}")
        reply: str = f"got msg '{text}' with rxSnr: {rx_snr} and hopLimit: {hop_limit}"
        print("Sending reply: ", reply)
        interface.sendText(reply)

def onConnection(interface: MeshInterface, topic: Any = pub.AUTO_TOPIC) -> None:  # pylint: disable=unused-argument
    """called when we (re)connect to the radio"""
    print("Connected. Will auto-reply to all messages while running.")

parser = argparse.ArgumentParser(description="Meshtastic Auto-Reply Feature Demo")
group = parser.add_mutually_exclusive_group()
group.add_argument("--host", help="Connect via TCP to this hostname or IP")
group.add_argument("--ble", help="Connect via BLE to this MAC address")

args = parser.parse_args()

pub.subscribe(onReceive, "meshtastic.receive")
pub.subscribe(onConnection, "meshtastic.connection.established")

iface: Optional[Union[
    meshtastic.tcp_interface.TCPInterface,
    meshtastic.ble_interface.BLEInterface,
    meshtastic.serial_interface.SerialInterface
]] = None

# defaults to serial, use --host for TCP or --ble for Bluetooth
try:
    if args.host:
        # note: timeout only applies after connection, not during the initial connect attempt
        # TCPInterface.myConnect() calls socket.create_connection() without a timeout
        iface = meshtastic.tcp_interface.TCPInterface(hostname=args.host, timeout=10)
    elif args.ble:
        iface = meshtastic.ble_interface.BLEInterface(address=args.ble, timeout=10)
    else:
        iface = meshtastic.serial_interface.SerialInterface(timeout=10)
except KeyboardInterrupt as exc:
    raise SystemExit(0) from exc
except Exception as e:
    print(f"Error: Could not connect. {e}")
    raise SystemExit(1) from e

try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    pass
finally:
    if iface:
        iface.close()
