#include "mqtt_client.h"

#ifndef QEMU_BUILD

#include <WiFi.h>
#include <PubSubClient.h>
#include <Preferences.h>
#include <ArduinoJson.h>
#include "state.h"
#include "config.h"

// --- Config ---
static bool     mqttActif    = false;
static String   mqttServer   = "192.168.1.36";
static uint16_t mqttPort     = 1883;
static String   mqttUser;
static String   mqttPass;
static String   mqttBase     = "solar";   // topic de base
static uint32_t mqttInterval = 30000;     // ms entre publications

// --- État runtime ---
static WiFiClient   wifiClient;
static PubSubClient mqttClient(wifiClient);
static bool         mqttConnecte      = false;
static unsigned long tDernierePubli   = 0;
static unsigned long tDerniereConnex  = 0;
static const unsigned long RECONNECT_INTERVAL = 15000UL;

// ---------------------------------------------------------------------------
// NVS
// ---------------------------------------------------------------------------

static void chargerNVS() {
    Preferences p;
    p.begin("mqtt", true);
    mqttActif    = p.getBool("enabled",  false);
    mqttServer   = p.getString("server", "192.168.1.36");
    mqttPort     = p.getUShort("port",   1883);
    mqttUser     = p.getString("user",   "");
    mqttPass     = p.getString("pass",   "");
    mqttBase     = p.getString("base",   "solar");
    mqttInterval = p.getULong("interval", 30000);
    p.end();
}

// ---------------------------------------------------------------------------
// Topics dérivés du topic de base
// ---------------------------------------------------------------------------

static String t(const char* suffixe) { return mqttBase + "/" + suffixe; }

// ---------------------------------------------------------------------------
// Callback réception (commande relais / entrées)
// ---------------------------------------------------------------------------

static void mqttCallback(char* topic, byte* payload, unsigned int len) {
    String msg;
    for (unsigned int i = 0; i < len; i++) msg += (char)payload[i];
    msg.trim();

    bool etat = (msg == "ON" || msg == "1" || msg == "true");

    String top = String(topic);
    if (top == t("relay/1/set")) {
        state.relay1 = etat;
        digitalWrite(PIN_RELAY1, etat ? HIGH : LOW);
        Serial.printf("[MQTT] Relais 1 → %s\n", etat ? "ON" : "OFF");
    } else if (top == t("relay/2/set")) {
        state.relay2 = etat;
        digitalWrite(PIN_RELAY2, etat ? HIGH : LOW);
        Serial.printf("[MQTT] Relais 2 → %s\n", etat ? "ON" : "OFF");
    }
}

// ---------------------------------------------------------------------------
// Connexion / reconnexion
// ---------------------------------------------------------------------------

static void connecterMqtt() {
    if (WiFi.status() != WL_CONNECTED) return;

    mqttClient.setServer(mqttServer.c_str(), mqttPort);
    mqttClient.setCallback(mqttCallback);
    mqttClient.setBufferSize(512);

    String clientId = "kc868-" + WiFi.macAddress();
    clientId.replace(":", "");

    bool ok;
    if (mqttUser.length() > 0) {
        ok = mqttClient.connect(clientId.c_str(), mqttUser.c_str(), mqttPass.c_str());
    } else {
        ok = mqttClient.connect(clientId.c_str());
    }

    if (ok) {
        mqttConnecte = true;
        Serial.printf("[MQTT] Connecté — %s:%u  base: %s  intervalle: %us\n",
                      mqttServer.c_str(), mqttPort,
                      mqttBase.c_str(), mqttInterval / 1000);
        // Abonnements commandes
        mqttClient.subscribe(t("relay/1/set").c_str());
        mqttClient.subscribe(t("relay/2/set").c_str());
        // Publication immédiate après connexion
        mqttPublierEtat();
    } else {
        mqttConnecte = false;
        Serial.printf("[MQTT] Échec connexion (rc=%d) — retry dans %lus\n",
                      mqttClient.state(), RECONNECT_INTERVAL / 1000);
    }
}

// ---------------------------------------------------------------------------
// Publication état complet
// ---------------------------------------------------------------------------

static void pub(const char* suffixe, float val, int decimales = 2) {
    char buf[16];
    dtostrf(val, 1, decimales, buf);
    mqttClient.publish(t(suffixe).c_str(), buf, true);
}

static void pub(const char* suffixe, bool val) {
    mqttClient.publish(t(suffixe).c_str(), val ? "ON" : "OFF", true);
}

static void pub(const char* suffixe, int val) {
    mqttClient.publish(t(suffixe).c_str(), String(val).c_str(), true);
}

void mqttPublierEtat() {
    if (!mqttConnecte || !mqttClient.connected()) return;

    // Panneau solaire
    pub("pv/voltage",        state.pv);
    pub("pv/current",        state.pvCurrent);

    // Batterie
    pub("battery/voltage",   state.battery);
    pub("battery/soc",       (int)state.batSOC);
    pub("battery/temperature", state.batTemperature, 1);
    pub("battery/status",    (int)state.batStatut);

    // Sortie de charge
    pub("load/voltage",      state.loadVoltage);
    pub("load/current",      state.loadCurrent);
    pub("load/power",        state.loadPower, 1);

    // Énergie
    pub("energy/generated/today", state.energieGenJour);
    pub("energy/generated/total", state.energieGenTotal);
    pub("energy/consumed/today",  state.energieConJour);
    pub("energy/consumed/total",  state.energieConTotal);

    // État général
    pub("sun",     state.sun);
    pub("rs485/ok", state.rs485_ok);

    // Relais
    pub("relay/1", state.relay1);
    pub("relay/2", state.relay2);

    // Entrées numériques
    pub("input/1", state.di1);
    pub("input/2", state.di2);

    tDernierePubli = millis();
}

// ---------------------------------------------------------------------------
// API publique
// ---------------------------------------------------------------------------

void initMqtt() {
    chargerNVS();
    Serial.printf("[MQTT] %s — %s:%u  base: %s  intervalle: %us\n",
                  mqttActif ? "Activé" : "Désactivé",
                  mqttServer.c_str(), mqttPort,
                  mqttBase.c_str(), mqttInterval / 1000);
}

void gererMqtt() {
    if (!mqttActif) return;
    if (WiFi.status() != WL_CONNECTED) {
        if (mqttConnecte) { mqttConnecte = false; }
        return;
    }

    if (!mqttClient.connected()) {
        mqttConnecte = false;
        unsigned long maintenant = millis();
        if ((maintenant - tDerniereConnex) >= RECONNECT_INTERVAL) {
            tDerniereConnex = maintenant;
            connecterMqtt();
        }
        return;
    }

    mqttClient.loop();

    // Publication périodique
    if ((millis() - tDernierePubli) >= mqttInterval) {
        mqttPublierEtat();
    }
}

bool setMqttConfig(bool enabled, const char* server, uint16_t port,
                   const char* user, const char* pass,
                   const char* topicBase, uint32_t intervalMs) {
    Preferences p;
    p.begin("mqtt", false);
    p.putBool("enabled",   enabled);
    p.putString("server",  server);
    p.putUShort("port",    port);
    p.putString("user",    user);
    p.putString("pass",    pass);
    p.putString("base",    topicBase);
    p.putULong("interval", intervalMs);
    p.end();

    if (mqttConnecte) { mqttClient.disconnect(); mqttConnecte = false; }
    tDerniereConnex = 0;
    chargerNVS();
    Serial.printf("[MQTT] Config mise à jour — %s\n", mqttActif ? "activé" : "désactivé");
    return true;
}

void getMqttJson(String &out) {
    JsonDocument doc;
    doc["enabled"]   = mqttActif;
    doc["connected"] = mqttConnecte;
    doc["server"]    = mqttServer;
    doc["port"]      = mqttPort;
    doc["user"]      = mqttUser;
    doc["pass"]      = mqttPass;
    doc["base"]      = mqttBase;
    doc["interval"]  = mqttInterval / 1000;   // en secondes pour l'UI
    serializeJson(doc, out);
}

#else
// --- Stubs QEMU ---
void initMqtt()            { Serial.println("[MQTT] Désactivé (build QEMU)"); }
void gererMqtt()           {}
void mqttPublierEtat()     {}
void getMqttJson(String &out) { out = "{\"enabled\":false,\"connected\":false}"; }
bool setMqttConfig(bool, const char*, uint16_t, const char*, const char*,
                   const char*, uint32_t) { return false; }
#endif
