import os
import signal
from time import sleep
import concurrent.futures as futures
from typing import Optional, Union, List
from datetime import datetime, timedelta, UTC
from pyqttier import Mqtt5Connection, MqttTransportType, MqttTransport
from {{stinger.python.package_name}}.client import {{stinger.python.client_class_name}}, {{stinger.python.client_class_name}}Builder, {{stinger.python.client_class_name}}Discoverer
from {{stinger.python.package_name}}.interface_types import *
import threading

{% if (stinger.methods|length + stinger.properties_rw|length) > 0%}
def request_loop(client: {{stinger.python.client_class_name}}):
    """Example request loop that runs in a separate thread."""
    sleep(30)
    while True:
        {%-for method_name, method in stinger.methods.items()%}
        print("Making call to '{{method_name|snake_case}}'")
        future_resp = client.{{method_name|snake_case}}({%for arg in method.arg_list%}{{arg.name|snake_case}}={{arg.get_random_example_value()}}{%if not loop.last%}, {%endif%}{%endfor%})
        try:
            print(f"RESULT:  {future_resp.result(5)}")
        except futures.TimeoutError:
            print(f"Timed out waiting for response to '{{method_name|snake_case}}' call")
        sleep(5)
        {%endfor%}{# for each method #}
        {%for prop_name, prop in stinger.properties_rw.items()%}
        {%if prop.arg_list|length > 1%}
        client.{{prop_name|snake_case}} = {{prop_name|UpperCamelCase}}Property(
            {%-for arg in prop.arg_list%}
            {{arg.name|snake_case}}={{arg.get_random_example_value()}},
            {%-endfor%}
        )
        {%else%}
        client.{{prop_name|snake_case}} = {{prop.arg_list[0].get_random_example_value()}}
        {%endif%} {# end propertly length cases #}
        {%endfor%} {# for each property #}
        sleep(10)
{%endif%} {# any methods or properties #}

if __name__ == '__main__':
    import sys

    if len(sys.argv) > 1 and sys.argv[1] == '--help':
        print("Usage: client_demo.py [--help]")
        print("Connects to an MQTT broker and demonstrates {{stinger.name}} client functionality.")
        print("Environment variables:")
        print("  MQTT_HOSTNAME - MQTT broker hostname (default: localhost)")
        print("  MQTT_PORT - MQTT broker port (default: 1883)")
        sys.exit(0)

    transport = MqttTransport(MqttTransportType.TCP, os.environ.get("MQTT_HOSTNAME", "localhost"), int(os.environ.get("MQTT_PORT", 1883)))
    conn = Mqtt5Connection(transport)

    client_builder = {{stinger.python.client_class_name}}Builder()
    {%for sig_name, sig in stinger.signals.items()%}
    @client_builder.receive_{{sig_name | snake_case }}
    def print_{{sig_name}}_receipt({%for arg in sig.arg_list%}{{arg.name}}: {{arg.python_annotation}}{%if not loop.last%}, {%endif%}{%endfor%}):
        """{{sig.description}}
        {%for arg in sig.arg_list-%}
        @param {{arg.name}} {{arg.python_annotation}} {{arg.description or ''}}
        {%endfor%}"""
        print(f"Got a '{{sig_name}}' signal: {%for arg in sig.arg_list%}{{arg.name}}={ {{arg.name}}{% if 'bytes' in arg.python_annotation %}!r{%endif-%} } {%endfor%}")
    {%endfor%}
    {%for prop_name, prop in stinger.properties.items()%}
    @client_builder.{{prop_name | snake_case}}_updated
    def print_new_{{prop_name}}_value(value: {{prop.python_annotation}}):
        """{{prop.description}}
        """
        print(f"Property '{{prop_name}}' has been updated to: {value{% if 'bytes' in prop.python_annotation %}!r{%endif-%} }")
    {%endfor%}

    discovery = {{stinger.python.client_class_name}}Discoverer(conn, client_builder)
    fut_client = discovery.get_singleton_client()
    try:
        client = fut_client.result(10)
    except futures.TimeoutError:
        print("Timed out waiting for a service to appear")
        exit(1)

    sleep(2)
    {% if (stinger.methods|length + stinger.properties_rw|length) > 0%}
    threading.Thread(target=request_loop, args=(client,), daemon=True).start()
    {%endif%}
    print("Ctrl-C will stop the program.")
    signal.pause()
