/**
 * @file mqttService.js
 * @author Radim Buráň (xburan03@stud.fit.vutbr.cz)
 */

const mqtt = require('mqtt');
const service = require('./recordsService');
const devicesService = require('./devicesServices');
require('dotenv').config();

const KEY_JSON_OUT_SOIL_HUMIDITY = "soil_hum"
const KEY_JSON_OUT_AIR_TEMPERATURE = "air_temp"
const KEY_JSON_OUT_AIR_HUMIDITY = "air_hum"
const KEY_JSON_OUT_WATER_LEVEL = "water_level"
const KEY_JSON_OUT_LIGHT = "light"

const KEYS_JSON_OUT = [
    KEY_JSON_OUT_SOIL_HUMIDITY,
    KEY_JSON_OUT_AIR_TEMPERATURE,
    KEY_JSON_OUT_AIR_HUMIDITY,
    KEY_JSON_OUT_WATER_LEVEL,
    KEY_JSON_OUT_LIGHT
]

const TOPIC_SENSORS = "esp32/sensors"   //ESP32 -> node
const TOPIC_ALIVE = "esp32/alive"        //ESP32 -> node -> (devID topic) -> esp32

const mqttHost = process.env.MQTT_IP
const mqttPort = process.env.MQTT_PORT

const mqttId = `NODE_${Math.random().toString(16).slice(3)}`
const topics = [
    TOPIC_ALIVE,
    TOPIC_SENSORS
]

const mqttUrl = `mqtt://${mqttHost}:${mqttPort}`

const mqttClient = mqtt.connect(mqttUrl, {
    mqttId,
    clean: true,
    connectTimeout: 4000,
    reconnectPeriod: 1000,
})

mqttClient.on('connect', () => {
    console.log('Connected')

    mqttClient.subscribe(topics, () => {
        console.log(`Subscribe to topic '${topics}'`)
    })
})

function writeToDB(device, measurement, value) {
    const data = {
        measurement: measurement,
        device: device
    }

    if (typeof value === "number") {
        data["float"] = value;
    }
    else {
        data["string"] = value;
    }

    service.write(data);
}

mqttClient.on('message', async (topic, payload) => {
    console.log('Received Message:', topic, payload.toString())

    if (topic === TOPIC_SENSORS) {
        await parseSensorData(topic, payload);
    }
    else if (topic === TOPIC_ALIVE) {
        await parseAliveTopic(topic, payload);
    }
})

async function checkDevice(json) {
    if (json.device != null) {
        console.log("Device: " + json.device);
        const deviceID = json.device;
        const dev = await devicesService.getByID(deviceID, {owner: false, preset: {
                select: {
                    deviceId: true,
                    id: true,
                    data: true
                }}});
        if (dev !== null && dev !== undefined) {
            return dev;
        }
        console.warn("Not a valid device! > " + deviceID);
    }
    return null;
}

async function parseAliveTopic(topic, payload) {
    let json = null;
    try {
        json = JSON.parse(payload.toString());
    } catch (e) {
        console.error(e);
        return;
    }

    let dev = await checkDevice(json)
    if (!dev) {
        return;
    }

    let presetData = dev?.preset?.data;
    let data = {
        date: Date.now() + 0.01,
        hum: presetData.humidity,
        hum_hyst: presetData.humidityHysteresis,
        pump_cycle: presetData.pumpCycle,
    }

    const now = new Date().getHours();
    const from = presetData.lightHourMin;
    const to = presetData.lightHourMax;

    if (to !== from && (to > from ? (now >= from && now < to) : (now >= from || now < to))) {
        data.luv = presetData.lightUV;
        data.lblue = presetData.lightBlue;
        data.lgreen = presetData.lightGreen;
        data.lyellow = presetData.lightYellow;
        data.lred = presetData.lightRed;
        data.lfred = presetData.lightFarRed;
        data.lir = presetData.lightIR;
    }
    else {
        data.luv = 0;
        data.lblue = 0;
        data.lgreen = 0;
        data.lyellow = 0;
        data.lred = 0;
        data.lfred = 0;
        data.lir = 0;
    }

    mqttClient.publish("esp32/devices/" + dev.id, JSON.stringify(data), {qos: 1});
}

async function parseSensorData(topic, payload) {
    let json = null;
    try {
        json = JSON.parse(payload.toString());
    } catch (e) {
        console.error(e);
        return;
    }

    KEYS_JSON_OUT.forEach(key => {
        console.log(key + ": " + json[key]);
    });

    let dev = await checkDevice(json)
    if (!dev) {
        return;
    }

    KEYS_JSON_OUT.forEach(key => {
        if (json.hasOwnProperty(key)) {
            writeToDB(dev.id, key, json[key]);
        }
    });
}

// app.get('/:msg', function (req, res) {
//   res.send("Hello World > " + req.params.msg)
//   mqttClient.publish(topicLed, req.params.msg)
// })

module.exports = {
    mqttClient
}