#include <ArduinoJson.h>
#include <SPIFFS.h>
#include "config.hpp"
#include "logger.h"

extern logging::Logger logger;

Config::Config()
{
    _filePath = "/config.json";
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", "Init config");
    if (!SPIFFS.begin(false))
    {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "CONFIG", "SPIFFS Mount Failed");
        return;
    } 
    readFile(SPIFFS, _filePath.c_str());
}

void Config::readFile(fs::FS &fs, const char *fileName)
{
    JsonDocument data;
    File configFile = fs.open(fileName, "r");
    DeserializationError error = deserializeJson(data, configFile);
    if (error)
    {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "CONFIG", "Failed to read file, using default configuration");
    }

    MqttConfig mqtt;
    mqtt.server = data["mqtt"]["server"].as<String>();
    mqtt.port = data["mqtt"]["port"].as<int>();
    mqtt.username = data["mqtt"]["username"].as<String>();
    mqtt.password = data["mqtt"]["password"].as<String>();
    mqtt.id = data["mqtt"]["id"].as<String>();

    LoraConfig lora;
    lora.frequency = data["lora"]["frequency"].as<long>();
    lora.spreadingFactor = data["lora"]["spreadingFactor"].as<int>();
    lora.signalBandwidth = data["lora"]["signalBandwidth"].as<long>();
    lora.codingRate4 = data["lora"]["codingRate4"].as<int>();
    lora.power = data["lora"]["power"].as<int>();
    logConfigInfo(data);
    configFile.close();
    
    mqttConfig = mqtt;
    loraConfig = lora;

    isConfigLoaded = true;
    
}

void Config::writeData()
{
    JsonDocument doc;

    JsonObject mqttJson = doc["mqtt"].to<JsonObject>();
    mqttJson["server"] =    this->mqttConfig.server;
    mqttJson["port"] =      this->mqttConfig.port;
    mqttJson["username"] =  this->mqttConfig.username;
    mqttJson["password"] =  this->mqttConfig.password;
    mqttJson["id"] =        this->mqttConfig.id;
    mqttJson["topic"] =     this->mqttConfig.topic;

    JsonObject lora = doc["lora"].to<JsonObject>();
    lora["frequency"] = 433775000;
    lora["spreadingFactor"] = 12;
    lora["signalBandwidth"] = 125000;
    lora["codingRate4"] = 5;
    lora["power"] = 20;
    
    // Delete existing file, otherwise the configuration is appended to the file
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", "Write DATA to file");
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("Server: " + String(this->mqttConfig.server)).c_str());
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("Port: " + String(this->mqttConfig.port)).c_str());
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("Username: " + String(this->mqttConfig.username)).c_str());
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("Password: " + String(this->mqttConfig.password)).c_str());
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("ID: " + String(this->mqttConfig.id)).c_str());
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", ("Topic: " + String(this->mqttConfig.topic)).c_str());


    SPIFFS.remove(_filePath);
    File configFile = SPIFFS.open(_filePath.c_str(), "w");
    if (!configFile)
    {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "CONFIG", "Failed to open file for writing");
        return;
    }

    // Serialize the JSON document to the file
    if (serializeJson(doc, configFile) == 0)
    {
        logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "CONFIG", "Failed to write to file");
    }
    
    logConfigInfo(doc);
    // Close the file
    configFile.close();
}

void Config::logConfigInfo(JsonDocument& doc) {
    logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", "Logging configuration data:");

    JsonObject rootJson = doc.as<JsonObject>();

    // Iterate over each key-value pair at the root level
    for (auto kvp : rootJson) {
        String key = kvp.key().c_str();
        String value;

        // Get the value associated with the key
        const JsonVariant& jsonValue = kvp.value();

        // Check the type of the JSON value and convert it to a string accordingly
        if (jsonValue.is<String>()) {
            value = jsonValue.as<String>();
        } else if (jsonValue.is<int>()) {
            value = String(jsonValue.as<int>());
        } else if (jsonValue.is<long>()) {
            value = String(jsonValue.as<long>());
        } else if (jsonValue.is<bool>()) {
            value = jsonValue.as<bool>() ? "true" : "false";
        } else if (jsonValue.is<JsonArray>() || jsonValue.is<JsonObject>()) {
            JsonDocument tempJson;
            tempJson.set(jsonValue);
            serializeJson(tempJson, value);
        } else if (jsonValue.isNull()) {
            value = "null";
        } else {
            value = "Unsupported data type";
        }

        logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "CONFIG", (key + ": " + value).c_str());
    }
}

void MqttConfig::setMqtt(const String &server, const int &port, const String &username, const String &password, const String &topic)
{
    this->server = server;
    this->port = port;
    this->username = username;
    this->password = password;
    this->topic = topic;
}

void MqttConfig::setId(const String &id)
{
    this->id = id;
}
