"""
Name: cloud_monitoring_client
Title: Cloud Monitoring Client
Author: Cooper
Date: 19/12/2023

Desc:   This is the client for the cloud monitoring gubbins.

"""
import time
import json
import logging
import threading
import posixpath
import platform

from hanip.itxpt import mqtt_client


class CloudMonitoringClient(object):
    def __init__(self, config_dict, hw_dict, debug=False):
        self.debug = debug
        self.config_dict = config_dict
        self.hw_dict = hw_dict

        #MQTT Topic stuff
        self.customer_id = None
        self.vehicle_id = None
        self.mqtt_inventory_topic_prefix = "hanover/inventory/equipment"
        self.mqtt_runtime_topic_prefix = "hanover/runtime"
        self.mqtt_inventory_topic = None
        self.mqtt_runtime_topic = None
        self.mqtt_device_type = None

        #MQTT Status stuff
        self.cloud_connected = False

        #Other necessary values
        self.os_version = "%s %s" % (platform.system(), platform.release())

    """
    ###################################################################################################################
    MQTT Functions
    """
    def create_mqtt_conn_dict(self) -> dict:
        """
        This creates the MQTT connection dictionary needed by the MQTT client.
        """
        mqtt_conn_dict = {
            "version": 311,
            "port": int(self.config_dict.get("CLOUD_port", 1883)),
            "username": self.config_dict.get("CLOUD_username", ""),
            "password": self.config_dict.get("CLOUD_password", ""),
            "discover": False,
            "fallback_address": self.config_dict.get("CLOUD_server"),
            "timeout": 0,
            "disconnect_timeout": 5,
            "address": "0",
            "broker_topic": "/hanover",
            "reply_topic": None,
            "status_topic": None
        }

        return mqtt_conn_dict

    def connect_to_cloud(self) -> None:
        """

        """
        self.mqtt_client = mqtt_client.MQTTConnectionHandler("Cloud", self.create_mqtt_conn_dict())
        self.mqtt_thread = threading.Thread(target=self.mqtt_client.run)
        self.mqtt_thread.start()

    def publish_payload(self, topic, payload):
        """

        """
        if self.mqtt_client.service_connected:
            if self.debug:
                print(topic)
                print(payload)
            self.mqtt_client.send_message(topic, payload)

    def setup_mqtt_topics(self) -> None:
        """
        This sets up the MQTT topics to be used by the rest of the class.  Example topics include:
        <Customer_ID>/<VIN>/hanover/inventory/equipment/extDisplay_00/info/device
        <Customer_ID>/<VIN>/hanover/inventory/equipment/EG4/info/sw
        <Customer_ID>/<VIN>/hanover/runtime/destination_info/outputs/dest_code

        """
        self.customer_id = self.config_dict.get("CLOUD_customer_id", "")
        self.vehicle_id = self.config_dict.get("CLOUD_vehicle_id", "1234")

        if self.hw_dict["hw_type"] == "con":
            self.mqtt_device_type = self.hw_dict["model"].upper()
        else:
            if self.hw_dict["hw_type"] == "hires":
                sign_type = "ext"
            else:
                sign_type = self.hw_dict["hw_type"]

            self.mqtt_device_type = "%sDisplay_%s" % (sign_type, self.hw_dict["address"].zfill(2))

        self.mqtt_inventory_topic = posixpath.join(self.customer_id, self.vehicle_id,
                                                   self.mqtt_inventory_topic_prefix,
                                                   self.mqtt_device_type)

        self.mqtt_runtime_topic = posixpath.join(self.customer_id, self.vehicle_id,
                                                 self.mqtt_runtime_topic_prefix)

        if self.debug:
            print(self.mqtt_device_type)
            print(self.mqtt_inventory_topic)
            print(self.mqtt_runtime_topic)

    """
    ###################################################################################################################
    Monitoring values - Inventory
    """

    def pub_serial_number(self) -> None:
        """
        This publishes the serial number payload to:
        - hanover/inventory/equipment/extDisplay_00/info/device
        - hanover/inventory/equipment/[ONION CONSOLE]]/info/device   TBD

        """
        topic = self.mqtt_inventory_topic + "/info/device"

        sn_dict = {
            "manufacturer": "Hanover Displays Ltd",
            "productType": "On Board Computer",
            "ProductName": self.hw_dict["model"],
            "variantName": "",
            "serialNumber": self.hw_dict["serial_number"],
            "hardwareVersion": self.hw_dict["hardware_version"],
            "dateOfManufacture": self.hw_dict["manufacture_date"],
            "ethernetPhys": [
                {
                "name": "eth0",
                "MAC": self.hw_dict.get("unit_MAC", "BEEF")
                }
            ]
            }

        serial = json.dumps(sn_dict, indent=4, separators=(',', ': '))
        self.publish_payload(topic, serial)

    def pub_sw_versions(self) -> None:
        """
        This publishes the SW versions to:
        - hanover/inventory/equipment/extDisplay_00/info/sw
        - hanover/inventory/equipment/[ONION CONSOLE]]/info/sw    TBD

        """
        topic = self.mqtt_inventory_topic + "/info/sw"

        sw_dict = {
            "baseFWversion": self.hw_dict["onion_ver"],
            "hostversion": self.hw_dict["software_version"],
            "OSversion": self.os_version,
            "appVersions": [
                {
                    "appName": "",
                    "version": ""
                },
            ]
        }

        sw = json.dumps(sw_dict, indent=4, separators=(',', ': '))
        self.publish_payload(topic, sw)

    """
    ###################################################################################################################
    Monitoring values - Journey
    """

    def generate_datetime(self) -> str:
        """
        This will generate the datetime string required in some payloads, but this assumes that the onion has a way to
        obtain the time, either via ITxPT or NTP

        It is in the following format:  2023-10-31T08:45:00+01:00
        """
        datatime_string = time.strftime("%Y-%m-%dT%H:%M%S%z")

        return datatime_string


    def pub_destcode(self, destCode: str):
        """
        hanover/runtime/destination_info/outputs/dest_code

        """
        topic = self.mqtt_runtime_topic + "/destination_info/outputs/destcode"

        dest_dict = {
            "atDateTime": self.generate_datetime(),
            "destCode": destCode
        }

        destcode = json.dumps(dest_dict, indent=4, separators=(',', ': '))
        self.publish_payload(topic, destcode)

    def pub_routecode(self, routeCode: str):
        """
        hanover/runtime/destination_info/outputs/route_code

        """
        topic = self.mqtt_runtime_topic + "/destination_info/outputs/routecode"

        dest_dict = {
            "atDateTime": self.generate_datetime(),
            "destCode": routeCode
        }

        destcode = json.dumps(dest_dict, indent=4, separators=(',', ': '))
        self.publish_payload(topic, destcode)


if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    print("Cloud client running in foreground")

    from hanip.onionip import onionConfig
    from hanip.onionip import hwDetermine

    oconf = onionConfig.OnionConfig()
    config_dict = oconf.parse_configs_dict(r"/etc/hanip", "config.cfg")

    hwd = hwDetermine.HardwareDeterminer("/dev/ttyS1", 115200, 38400, r"/etc/hanip")
    hw_dict = hwd.get_hardware_dict()
    print(hw_dict)

    #Add in software versions if running standalone
    hw_dict["onion_ver"] = "2.11.0"

    cmc = CloudMonitoringClient(config_dict, hw_dict, True)
    cmc.setup_mqtt_topics()

    cmc.connect_to_cloud()

    while 1:
        cmc.pub_serial_number()
        cmc.pub_sw_versions()
        time.sleep(3)
