#include <WiFi.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <Preferences.h>
#include <ArduinoJson.h>
#include <Arduino.h>
#include "config.h"
#include "wifi_ap.h"

static DNSServer dnsServer;

// --- mDNS ---
static String mdnsHostname = "pv";   // default → pv.local

static void chargerMdnsNVS() {
    Preferences p;
    p.begin("mdns", true);
    mdnsHostname = p.getString("host", "pv");
    p.end();
    // nettoyer : uniquement alphanum + tirets, lowercase
    String clean;
    for (char c : mdnsHostname) {
        c = tolower(c);
        if (isalnum(c) || c == '-') clean += c;
    }
    if (clean.isEmpty()) clean = "pv";
    mdnsHostname = clean;
}

static void demarrerMdns() {
    if (MDNS.begin(mdnsHostname.c_str())) {
        MDNS.addService("http", "tcp", 80);
        Serial.printf("[mDNS] Accessible sur http://%s.local\n", mdnsHostname.c_str());
    } else {
        Serial.println("[mDNS] Échec démarrage");
    }
}

// --- État STA ---
static bool          staConfiguree     = false;
static bool          staConnectee      = false;
static String        staSSID;
static String        staPass;
static unsigned long tDerniereTentative = 0;
static const unsigned long RECONNECT_INTERVAL_MS = 30000UL;

// ---------------------------------------------------------------------------
// NVS helpers
// ---------------------------------------------------------------------------

static void chargerCredentialsNVS() {
    Preferences p;
    p.begin("wifi_sta", true);
    staSSID = p.getString("ssid", "");
    staPass = p.getString("pass", "");
    p.end();
    staConfiguree = staSSID.length() > 0;
}

static void sauvegarderCredentialsNVS(const char *ssid, const char *pass) {
    Preferences p;
    p.begin("wifi_sta", false);
    p.putString("ssid", ssid);
    p.putString("pass", pass);
    p.end();
}

static void effacerCredentialsNVS() {
    Preferences p;
    p.begin("wifi_sta", false);
    p.remove("ssid");
    p.remove("pass");
    p.end();
}

// ---------------------------------------------------------------------------
// Événements WiFi
// ---------------------------------------------------------------------------

static void onWifiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
    switch (event) {
        case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
            Serial.printf("[WiFi][AP] Client connecté   — MAC %02X:%02X:%02X:%02X:%02X:%02X  clients:%d\n",
                          info.wifi_ap_staconnected.mac[0], info.wifi_ap_staconnected.mac[1],
                          info.wifi_ap_staconnected.mac[2], info.wifi_ap_staconnected.mac[3],
                          info.wifi_ap_staconnected.mac[4], info.wifi_ap_staconnected.mac[5],
                          WiFi.softAPgetStationNum());
            break;

        case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
            Serial.printf("[WiFi][AP] Client déconnecté — clients restants:%d\n",
                          WiFi.softAPgetStationNum());
            break;

        case ARDUINO_EVENT_WIFI_STA_GOT_IP:
            staConnectee = true;
            Serial.printf("[WiFi][STA] Connecté — SSID:%s  IP:%s  RSSI:%d dBm\n",
                          staSSID.c_str(),
                          WiFi.localIP().toString().c_str(),
                          WiFi.RSSI());
            break;

        case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
            if (staConnectee) {
                staConnectee        = false;
                tDerniereTentative  = millis();
                Serial.printf("[WiFi][STA] Déconnecté de %s — nouvelle tentative dans %lus\n",
                              staSSID.c_str(), RECONNECT_INTERVAL_MS / 1000);
            }
            break;

        default:
            break;
    }
}

// ---------------------------------------------------------------------------
// Démarrage
// ---------------------------------------------------------------------------

void demarrerWifi() {
    chargerMdnsNVS();
    WiFi.onEvent(onWifiEvent);

    // AP + STA simultanés — l'AP est toujours actif comme filet de sécurité
    WiFi.mode(WIFI_AP_STA);
    WiFi.softAPConfig(WIFI_IP, WIFI_GATEWAY, WIFI_SUBNET);

    if (strlen(WIFI_PASSWORD) > 0) {
        WiFi.softAP(WIFI_SSID, WIFI_PASSWORD);
    } else {
        WiFi.softAP(WIFI_SSID);
    }

    Serial.printf("[WiFi][AP] Démarré — SSID:%s  IP:%s\n",
                  WIFI_SSID, WiFi.softAPIP().toString().c_str());

    // Captive portal — tous les noms DNS → 192.168.4.1 pour les clients AP
    dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
    dnsServer.start(53, "*", WIFI_IP);

    demarrerMdns();

    // Tentative de connexion STA si credentials sauvegardés
    chargerCredentialsNVS();
    if (staConfiguree) {
        Serial.printf("[WiFi][STA] Tentative de connexion à %s…\n", staSSID.c_str());
        WiFi.begin(staSSID.c_str(), staPass.c_str());
        tDerniereTentative = millis();
    }
}

// ---------------------------------------------------------------------------
// Boucle principale (remplace traiterDNS)
// ---------------------------------------------------------------------------

void gererWifi() {
    dnsServer.processNextRequest();

    // Reconnexion automatique STA si configuré et déconnecté
    if (staConfiguree && !staConnectee) {
        unsigned long maintenant = millis();
        if ((maintenant - tDerniereTentative) >= RECONNECT_INTERVAL_MS) {
            tDerniereTentative = maintenant;
            Serial.printf("[WiFi][STA] Reconnexion à %s…\n", staSSID.c_str());
            WiFi.disconnect(false);
            WiFi.begin(staSSID.c_str(), staPass.c_str());
        }
    }
}

// ---------------------------------------------------------------------------
// API publique — connexion / déconnexion
// ---------------------------------------------------------------------------

void connecterWifiSTA(const char *ssid, const char *pass) {
    staSSID        = ssid;
    staPass        = pass;
    staConfiguree  = true;
    staConnectee   = false;
    tDerniereTentative = millis();

    sauvegarderCredentialsNVS(ssid, pass);

    WiFi.disconnect(false);
    delay(100);
    WiFi.begin(ssid, pass);
    Serial.printf("[WiFi][STA] Connexion lancée vers %s\n", ssid);
}

void deconnecterWifiSTA() {
    staConfiguree = false;
    staConnectee  = false;
    staSSID       = "";
    staPass       = "";

    effacerCredentialsNVS();
    WiFi.disconnect(true);
    Serial.println("[WiFi][STA] Credentials effacés, STA déconnectée");
}

// ---------------------------------------------------------------------------
// API publique — scan réseaux
// ---------------------------------------------------------------------------

void scannerReseauxJson(String &out) {
    int n = WiFi.scanNetworks(false, false);   // synchrone, pas de réseaux cachés

    JsonDocument doc;
    doc["ok"] = (n >= 0);

    if (n < 0) {
        doc["erreur"] = "Scan échoué";
        serializeJson(doc, out);
        return;
    }

    JsonArray arr = doc["networks"].to<JsonArray>();
    for (int i = 0; i < n; i++) {
        String s = WiFi.SSID(i);
        if (s.isEmpty()) continue;        // ignorer réseaux cachés
        JsonObject net = arr.add<JsonObject>();
        net["ssid"]    = s;
        net["rssi"]    = WiFi.RSSI(i);
        net["secured"] = (WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
    }

    WiFi.scanDelete();
    serializeJson(doc, out);
}

// ---------------------------------------------------------------------------
// API publique — statut JSON
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// API publique — mDNS
// ---------------------------------------------------------------------------

String getMdnsHostname() {
    return mdnsHostname;
}

bool setMdnsHostname(const char *nom) {
    String clean;
    for (const char *p = nom; *p; p++) {
        char c = tolower(*p);
        if (isalnum(c) || c == '-') clean += c;
    }
    if (clean.isEmpty() || clean.length() > 63) return false;

    mdnsHostname = clean;
    Preferences prefs;
    prefs.begin("mdns", false);
    prefs.putString("host", mdnsHostname);
    prefs.end();

    MDNS.end();
    demarrerMdns();
    return true;
}

void getWifiStatusJson(String &out) {
    JsonDocument doc;

    // AP
    JsonObject ap  = doc["ap"].to<JsonObject>();
    ap["ssid"]     = WIFI_SSID;
    ap["ip"]       = WiFi.softAPIP().toString();
    ap["clients"]  = WiFi.softAPgetStationNum();

    // STA
    JsonObject sta    = doc["sta"].to<JsonObject>();
    sta["configured"] = staConfiguree;
    sta["connected"]  = staConnectee;
    sta["ssid"]       = staSSID;
    sta["ip"]         = staConnectee ? WiFi.localIP().toString() : String("");
    sta["rssi"]       = staConnectee ? WiFi.RSSI() : 0;

    doc["mode"] = staConnectee ? "AP+STA" : (staConfiguree ? "AP (STA tentative)" : "AP");

    serializeJson(doc, out);
}
