import paho.mqtt.client as mqtt
import time
import sys
import os # To get process ID for easy killing

# --- Configuration ---
broker_address = "localhost"  # Replace with your MQTT broker IP/hostname
broker_port = 1883
client_id = "simple_will_tester"
keepalive_interval = 60  # seconds

# --- Will Message Details ---
will_topic = f"client_status/{client_id}/will"
will_payload = "Simple Will Tester Client Unexpectedly Disconnected"
will_qos = 1
will_retain = True

# --- Callbacks (minimal) ---
def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print(f"CONNECTED to broker: {broker_address} as client ID: {client_id}")
        print(f"  Session present flag: {flags.session_present}")
        print(f"  Process ID: {os.getpid()}. Use 'kill -9 {os.getpid()}' to test Will Message.")
        # The Will is now registered with the broker for this connection.
    else:
        print(f"CONNECTION FAILED with result code {rc}: {mqtt.error_string(rc)}")
        # Attempt to stop the loop and exit if connection failed on start
        client.loop_stop() # Ensure loop stops if connect fails in a way that doesn't exit
        sys.exit(f"Exiting due to connection failure.")

def on_disconnect(client, userdata, rc, properties=None):
    # This callback will likely NOT be triggered if you use 'kill -9'.
    # It's for graceful disconnections or network issues detected by Paho.
    if rc == 0:
        print("DISCONNECTED gracefully.")
    else:
        print(f"DISCONNECTED unexpectedly with result code {rc}. Will message *should* be sent by broker if not graceful.")

# --- Main Script ---
if __name__ == "__main__":
    print("Starting ultra-simple Will Message tester client...")

    # 1. Create a new MQTT client instance
    # Using CallbackAPIVersion.VERSION2 for compatibility with newer Paho versions
    # clean_session=False is important for your scenario
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id, clean_session=False)

    
    # 2. Assign callbacks
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect

    # 3. Set the Will Message
    # This configures the client instance. These details are sent with the CONNECT packet.
    print(f"Setting Will: Topic='{will_topic}', Payload='{will_payload}', QoS={will_qos}, Retain={will_retain}")
    client.will_set(
        topic=will_topic,
        payload=will_payload,
        qos=will_qos,
        retain=will_retain
    )

    # 4. Connect to the broker
    print(f"Attempting to connect to broker at {broker_address}:{broker_port}...")
    try:
        # The `connect` call sends the CONNECT packet, which includes the Will details.
        client.connect(broker_address, broker_port, keepalive_interval)
    except Exception as e:
        print(f"Error during initial connect attempt: {e}")
        sys.exit(1)

    # 5. Start the network loop and block until client is disconnected
    # `loop_forever()` is a blocking call that handles PINGREQ/PINGRESP,
    # and processes callbacks. It will exit if the client disconnects.
    try:
        client.loop_forever()
    except KeyboardInterrupt:
        print("\nCtrl+C received. Disconnecting client gracefully...")
        # `loop_forever` should exit on KeyboardInterrupt, then we can disconnect.
    except Exception as e:
        # This might catch errors if the loop is broken by other means
        print(f"An error occurred in the main loop: {e}")
    finally:
        # This 'finally' block might not execute fully or at all if 'kill -9' is used.
        # `disconnect()` sends a DISCONNECT packet, which PREVENTS the Will from being sent.
        # Only call this for a graceful shutdown like Ctrl+C.
        if sys.exc_info()[0] == KeyboardInterrupt: # Check if it was a KeyboardInterrupt
             client.disconnect() # Gracefully disconnect
        print("Client has stopped.")
