#include <WiFi.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#include <Arduino.h>
#include <esp_sleep.h>
#include "config.h"
#include "state.h"

// Persisté en mémoire RTC — survit au deep sleep, perdu au power-off complet
RTC_DATA_ATTR static bool     rtcSleepActif  = false;  // désactivé par défaut
RTC_DATA_ATTR static uint32_t rtcIntervalle  = 600;    // secondes entre réveil
RTC_DATA_ATTR static float    rtcSeuilSoleil = 2.0f;   // V PV minimum = jour
RTC_DATA_ATTR static bool     rtcRelay1      = false;  // état relais sauvegardé
RTC_DATA_ATTR static bool     rtcRelay2      = false;

// Runtime
static bool          enModeNuit = false;
static unsigned long tDebutNuit = 0;
#define TEMPO_CONFIRMATION_NUIT  60000UL  // 60s de nuit confirmée avant de dormir
#define FICHIER_SLEEP            "/sleep.json"

// --- Utilitaires ---

static uint16_t crc16Modbus(const uint8_t *buf, int len) {
    uint16_t crc = 0xFFFF;
    for (int i = 0; i < len; i++) {
        crc ^= buf[i];
        for (int b = 0; b < 8; b++)
            crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
    }
    return crc;
}

// Lecture synchrone de la tension PV via Modbus RTU brut
// Utilisé uniquement au réveil, avant que le serveur web soit démarré
static float lirePVSync() {
    uint8_t req[8] = { MODBUS_ADRESSE, 0x04, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00 };
    uint16_t crc = crc16Modbus(req, 6);
    req[6] = crc & 0xFF;
    req[7] = crc >> 8;

    while (Serial2.available()) Serial2.read();  // vider buffer résiduel
    Serial2.write(req, 8);
    Serial2.flush();

    unsigned long t = millis();
    while (Serial2.available() < 7 && millis() - t < 300);
    if (Serial2.available() < 7) return -1.0f;

    uint8_t resp[7];
    Serial2.readBytes(resp, 7);
    if (resp[0] != MODBUS_ADRESSE || resp[1] != 0x04 || resp[2] != 2) return -1.0f;
    return ((resp[3] << 8) | resp[4]) * 0.01f;
}

static void entrerEnDeepSleep() {
    Serial.printf("Deep sleep — réveil dans %ds\n", rtcIntervalle);
    Serial.flush();
    WiFi.mode(WIFI_OFF);
    delay(50);
    esp_sleep_enable_timer_wakeup((uint64_t)rtcIntervalle * 1000000ULL);
    esp_deep_sleep_start();
    // Ne revient jamais ici
}

// --- API publique ---

void verifierEtDormirSiNuit() {
    if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER) return;
    if (!rtcSleepActif) return;

    Serial.println("Réveil timer — vérification ensoleillement...");
    Serial2.begin(9600, SERIAL_8N1, PIN_RS485_RX, PIN_RS485_TX);
    delay(50);
    float pv = lirePVSync();
    Serial2.end();

    Serial.printf("PV = %.2fV (seuil %.1fV)\n", pv, rtcSeuilSoleil);

    if (pv >= 0.0f && pv < rtcSeuilSoleil) {
        Serial.println("Toujours nuit → re-sleep");
        entrerEnDeepSleep();  // ne revient pas
    }
    Serial.println("Jour détecté → démarrage complet");
}

void restaurerRelais() {
    if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_TIMER) return;
    state.relay1 = rtcRelay1;
    state.relay2 = rtcRelay2;
    digitalWrite(PIN_RELAY1, rtcRelay1 ? HIGH : LOW);
    digitalWrite(PIN_RELAY2, rtcRelay2 ? HIGH : LOW);
    Serial.printf("Relais restaurés — R1:%d R2:%d\n", rtcRelay1, rtcRelay2);
}

void chargerConfigSleep() {
    if (!LittleFS.exists(FICHIER_SLEEP)) return;
    File f = LittleFS.open(FICHIER_SLEEP, "r");
    if (!f) return;
    JsonDocument doc;
    if (!deserializeJson(doc, f)) {
        rtcSleepActif  = doc["actif"]      | false;
        rtcIntervalle  = doc["intervalle"] | 600u;
        rtcSeuilSoleil = doc["seuil"]      | 2.0f;
    }
    f.close();
    Serial.printf("Sleep config — actif:%d intervalle:%ds seuil:%.1fV\n",
                  rtcSleepActif, rtcIntervalle, rtcSeuilSoleil);
}

void gererSleep() {
    if (!rtcSleepActif) return;
    if (!state.rs485_ok) return;  // pas de données fiables — ne pas dormir

    unsigned long maintenant = millis();

    if (!state.sun) {
        if (!enModeNuit) {
            enModeNuit = true;
            tDebutNuit = maintenant;
            Serial.printf("Nuit — sleep dans %lus si confirmé\n", TEMPO_CONFIRMATION_NUIT / 1000);
            return;
        }
        if (maintenant - tDebutNuit < TEMPO_CONFIRMATION_NUIT) return;

        rtcRelay1 = state.relay1;  // sauvegarder état relais en RTC
        rtcRelay2 = state.relay2;
        entrerEnDeepSleep();       // ne revient pas
    } else {
        enModeNuit = false;
    }
}

void getSleepConfigJson(String &out) {
    JsonDocument doc;
    doc["actif"]     = rtcSleepActif;
    doc["intervalle"] = rtcIntervalle;
    doc["seuil"]     = rtcSeuilSoleil;
    serializeJson(doc, out);
}

bool setSleepConfig(bool actif, uint32_t intervalle, float seuil) {
    rtcSleepActif  = actif;
    rtcIntervalle  = intervalle;
    rtcSeuilSoleil = seuil;
    File f = LittleFS.open(FICHIER_SLEEP, "w");
    if (!f) return false;
    JsonDocument doc;
    doc["actif"]      = actif;
    doc["intervalle"] = intervalle;
    doc["seuil"]      = seuil;
    serializeJson(doc, f);
    f.close();
    return true;
}
