"""
Generated by Stinger-IPC code generator {{generator.version}}.
"""

from time import sleep
import signal
import os
from typing import Optional, Union, List
from datetime import datetime, timedelta, UTC
from pyqttier import Mqtt5Connection, MqttTransportType, MqttTransport
from {{stinger.python.package_name}}.server import {{stinger.python.server_class_name}}
{% if stinger.properties|length > 0 -%}
from {{stinger.python.package_name}}.property import {%if config.properties.stinger_owned_values %}{{stinger.name|UpperCamelCase}}InitialPropertyValues{%else%}{{stinger.name|UpperCamelCase}}PropertyAccess{%endif%}
{%endif%} {#- end has properties -#}
from {{stinger.python.package_name}}.interface_types import *

{%if (stinger.properties|length > 0) and (not config.properties.stinger_owned_values) %}
class {{stinger.name|UpperCamelCase}}PropertyOwnership:
    def __init__(self):
        {%-for prop_name, prop in stinger.properties.items()%}
        self._{{prop_name|snake_case}} = {##}
            {%-if prop.arg_list|length > 1 -%}
            {{prop_name|UpperCamelCase}}Property(
                {%-for arg in prop.arg_list%}
                {{arg.name|snake_case}}={{arg.get_random_example_value()}},
                {%-endfor%}
            )
            {%-else -%} {# else single value property #}
            {{-prop.arg_list[0].get_random_example_value()}}
            {%-endif %} {# end property value cases #}
        {%-endfor%} {# end property foreach #}

    {%-for prop_name, prop in stinger.properties.items()%}
    def get_property_{{prop_name|snake_case}}(self):
        """Return the value for the '{{prop_name|snake_case}}' property."""
        return self._{{prop_name|snake_case}}

    {%-if not prop.read_only%}
    def set_property_{{prop_name|snake_case}}(self, value: {{prop.python_annotation}}):
        """Set the value for the '{{prop_name|snake_case}}' property."""
        print(f"~~> Received update for '{{prop_name}}' property: {value}")
        self._{{prop_name|snake_case}} = value
    {%-endif%} {# end read only #}
    {%endfor%} {# end property foreach #}
{%endif%}{# end has some app-owned values #}

if __name__ == '__main__':
    """
    This shows an example on how to run the code.  Ideally, your app should do something similar, but use the methods in
    a more meaningful way.
    """

    import sys

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

    {%if stinger.properties | length > 0 %}
    {% if config.properties.stinger_owned_values %}
    initial_property_values = {{stinger.name|UpperCamelCase}}InitialPropertyValues(
        {%- for prop_name, prop in stinger.properties.items() %}
        {{prop_name | snake_case}}=
            {%-if prop.arg_list | length > 1%}
            {{prop.python_class}}(
                {%for arg in prop.arg_list%}
                {{arg.name|snake_case}}={{arg.get_random_example_value()}},
                {%endfor%}
            ),
            {%-else-%}
            {{prop.arg_list[0].get_random_example_value()}},
            {%endif%}
        {{prop_name | snake_case}}_version={{loop.index}},
        {%endfor%}{# end foreach property#}
    )
    {%else%} {# app-owned properties #}
    property_owner = {{stinger.name|UpperCamelCase}}PropertyOwnership()
    property_access = {{stinger.name|UpperCamelCase}}PropertyAccess(
        {%for prop_name, prop in stinger.properties.items()%}
        {{prop_name | snake_case}}_getter=property_owner.get_property_{{prop_name | snake_case}},
        {%-if not prop.read_only%}
        {{prop_name | snake_case}}_setter=property_owner.set_property_{{prop_name | snake_case}},
        {%-endif%}
        {%endfor%}{# end foreach property#}
    )
    {%endif%} {# end stinger/app owned properties#}
    {%endif%} {# end has properties #}
    
    transport = MqttTransport(MqttTransportType.TCP, os.environ.get("MQTT_HOSTNAME", "localhost"), int(os.environ.get("MQTT_PORT", 1883)))
    conn = Mqtt5Connection(transport, client_id=os.environ.get("CLIENT_ID", "py-server-demo"))
    server = {{stinger.python.server_class_name}}(conn, os.environ.get("SERVICE_ID", "py-server-demo:1"){%if stinger.properties | length > 0 %}, {% if config.properties.stinger_owned_values %}initial_property_values{%else%}property_access{%endif%}{%endif%}{%for topic_param in config.topics.params %}, {{topic_param|snake_case}}="example"{% endfor %})

    {%for method_name, method in stinger.methods.items()%}
    @server.handle_{{method_name | snake_case}} 
    def {{method_name | snake_case}}({%for arg in method.arg_list%}{{arg.name}}: {{arg.python_annotation}}{%if not loop.last%}, {%endif%}{%endfor%}) -> {{method.return_value_python_annotation}}:
        """ This is an example handler for the '{{method_name}}' method.  """
        print(f"--> Running {{method_name | snake_case}}({%for arg in method.arg_list %}{ {{-arg.name-}}{% if 'bytes' in arg.python_annotation %}!r{%endif-%} }{%if not loop.last%}, {%endif%}{%endfor%})'")
        return {{method.get_return_value_random_example_value('python')}}
    {%endfor%}
    {%if config.properties.stinger_owned_values %}
    {%for prop_name, prop in stinger.properties.items()%}
    @server.on_{{prop_name | snake_case}}_updated
    def on_{{prop_name | snake_case}}_update({%for arg in prop.arg_list %}{{arg.name}}: {{arg.python_annotation}}{%if not loop.last%}, {%endif%}{%endfor%}):
        """ Example callback for when the '{{prop_name}}' property is updated. """
        print(f"~~> Received update for '{{prop_name}}' property: {%for arg in prop.arg_list %}{ {{arg.name}}= }{%if not loop.last%}, {%endif%}{%endfor%}")
    {%endfor%}
    {%endif%}

    print("Ctrl-C will stop the program.")

    while True:
        try:
            {%for sig_name, sig in stinger.signals.items()-%}
            server.emit_{{sig_name|snake_case}}({%for arg in sig.arg_list%}{{arg.get_random_example_value()}}{%if not loop.last%}, {%endif%}{%endfor%})
            {%endfor%}
            sleep(4)
            {%for sig_name, sig in stinger.signals.items()-%}
            server.emit_{{sig_name|snake_case}}({%for arg in sig.arg_list%}{{arg.name|snake_case}}={{arg.get_random_example_value()}}{%if not loop.last%}, {%endif%}{%endfor%})
            {%endfor%}
            sleep(42)
        except KeyboardInterrupt:
            break

    signal.pause()
