#include "mqtt.hpp"
#include "config.hpp"
#include <PubSubClient.h>
#include "logger.h"

extern logging::Logger logger;

extern Config config;
extern Config config;

Mqtt::Mqtt(WiFiClient& wifiClient) : mqttClient(wifiClient) {}

void Mqtt::mqttSetup()
{
    String message = "MqttServer: " + config.mqttConfig.server + ", MqttPort: " + config.mqttConfig.port;
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
    if (config.mqttConfig.server.length() > 0)
    {
        // strcpy(mqttHost, config.mqtt.server.c_str());
        String message = "MqttServer: " + config.mqttConfig.server + ", MqttPort: " + config.mqttConfig.port;
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
        IPAddress mqttServerIP;
        if(mqttServerIP.fromString(config.mqttConfig.server)){
            
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", mqttServerIP.toString().c_str());
            mqttClient.setServer(mqttServerIP, config.mqttConfig.port);
        }else{
            logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "MQTT", "MQTT Invalid IP address format %s", config.mqttConfig.server);
        }
        mqttClient.setCallback(callback);
    }
    else
    {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "MQTT", "MQTT config is not set");
    }
}

void Mqtt::mqttRun()
{
    delay(100);
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "MQTT RUN");
    if (WiFi.status() == WL_CONNECTED && !mqttClient.loop()) {
        mqttReconnect();
    } 
}

void Mqtt::mqttReconnect()
{
    int retryCount = 0;
    
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Attempting MQTT connection...");
    // Loop until we're reconnected
    
    while (!mqttClient.connected() && retryCount < 10)
    {
        // Attempt to connect
        String mqttId = config.mqttConfig.id;
        if (mqttId.isEmpty())
        {
            mqttId = "ESP32-";
            mqttId += String(random(0xffff), HEX);
            
            logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Set ID: %s", mqttId );
            config.setMqttId(mqttId);
            config.writeData();
        }

        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Server: %s", config.mqttConfig.server.c_str());
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Port: %d", config.mqttConfig.port);
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Username: %s", config.mqttConfig.username.c_str());
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Password: %s", config.mqttConfig.password.c_str());
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "ID: %s", config.mqttConfig.id.c_str());
  
        if (mqttClient.connect(mqttId.c_str(), "simon", "bajsa123"))
        {
            logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "connected");
            String topic = config.mqttConfig.topic;
            if (topic.isEmpty())
            {
                topic = config.mqttConfig.id;
            }
            mqttClient.subscribe(topic.c_str());
            String message = "Subscribe to: " + topic;
            logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
        }
        else
        {
            logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, "MQTT", "Not connected to mqtt, try again in 5 s.");
            // Wait 5 seconds before retrying
            delay(5000);
        }
    }
    // Check if we're connected after the loop
    if (!mqttClient.connected()) {
        // Connection failed after maximum retries
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "MQTT", "Failed to connect to MQTT broker after 10 retries");
    }
}

void Mqtt::callback(char *topic, uint8_t *payload, unsigned int length)
// Not implemented yet. Just for debug
{
    // Create spot in memory for message
    char message[length + 1];
    // Write payload to String
    strncpy(message, (char *)payload, length);
    // Nullify last character to eliminate garbage at end
    message[length] = '\0';

    // Create correct object

    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Received: %s", message);
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "From: %s", topic);
}

void Mqtt::mqttPublish(const char* topic, const char* payload){
     if (mqttClient.connected()) {
        mqttClient.publish(topic, payload);
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Published message to %s: %s", topic, payload);
    } else {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "MQTT", "Failed to publish message. MQTT client not connected.");
    }
}
