#include "wireguard_vpn.h"

#ifndef QEMU_BUILD

#include <WireGuard-ESP32.h>
#include <WiFi.h>
#include <Preferences.h>
#include <ArduinoJson.h>

static WireGuard wg;

static bool     wgActif    = false;
static bool     wgConnecte = false;
static String   wgPrivKey;
static String   wgPubKey;
static String   wgPsk;
static String   wgEndpoint;
static uint16_t wgPort      = 51820;
static String   wgLocalIP;
static uint16_t wgKeepalive = 25;

static unsigned long tDerniereConnexion = 0;
static const unsigned long WG_RETRY_MS  = 60000UL;

// Valeurs initiales — à renseigner via l'interface web (onglet Config > VPN WireGuard)
static const char* WG_DEFAULT_PRIVKEY   = "";
static const char* WG_DEFAULT_PUBKEY    = "";
static const char* WG_DEFAULT_PSK       = "";
static const char* WG_DEFAULT_ENDPOINT  = "";
static const uint16_t WG_DEFAULT_PORT   = 51820;
static const char* WG_DEFAULT_LOCALIP   = "";
static const uint16_t WG_DEFAULT_KEEPALIVE = 25;

static void chargerWgNVS() {
    Preferences p;
    p.begin("wireguard", true);
    wgActif     = p.getBool("enabled",    false);
    wgPrivKey   = p.getString("privkey",  "");
    wgPubKey    = p.getString("pubkey",   "");
    wgPsk       = p.getString("psk",      "");
    wgEndpoint  = p.getString("endpoint", "");
    wgPort      = p.getUShort("port",     51820);
    wgLocalIP   = p.getString("localip",  "");
    wgKeepalive = p.getUShort("keepalive", 25);
    p.end();
}

static void ecrireDefauts() {
    Preferences p;
    p.begin("wireguard", false);
    p.putBool("enabled",    false);
    p.putString("privkey",  WG_DEFAULT_PRIVKEY);
    p.putString("pubkey",   WG_DEFAULT_PUBKEY);
    p.putString("psk",      WG_DEFAULT_PSK);
    p.putString("endpoint", WG_DEFAULT_ENDPOINT);
    p.putUShort("port",     WG_DEFAULT_PORT);
    p.putString("localip",  WG_DEFAULT_LOCALIP);
    p.putUShort("keepalive", WG_DEFAULT_KEEPALIVE);
    p.end();
}

static void connecterWg() {
    if (wgPrivKey.isEmpty() || wgPubKey.isEmpty() || wgEndpoint.isEmpty() || wgLocalIP.isEmpty()) {
        Serial.println("[WireGuard] Config incomplète — connexion annulée");
        return;
    }

    // Extraire l'IP (supprimer le préfixe /24 si présent)
    String ipStr = wgLocalIP;
    int slash = ipStr.indexOf('/');
    if (slash > 0) ipStr = ipStr.substring(0, slash);

    IPAddress ip;
    if (!ip.fromString(ipStr)) {
        Serial.printf("[WireGuard] IP locale invalide : %s\n", wgLocalIP.c_str());
        return;
    }

    Serial.printf("[WireGuard] Connexion → %s:%u  IP locale: %s\n",
                  wgEndpoint.c_str(), wgPort, ipStr.c_str());

    bool ok = wg.begin(ip, wgPrivKey.c_str(), wgEndpoint.c_str(),
                       wgPubKey.c_str(), wgPort);

    if (ok) {
        wgConnecte = true;
        Serial.println("[WireGuard] Tunnel établi");
    } else {
        wgConnecte = false;
        tDerniereConnexion = millis();
        Serial.println("[WireGuard] Échec connexion — nouvelle tentative dans 60s");
    }
}

void initWireGuard() {
    // Premier boot : écrire les défauts issus de kc868-a2.conf si NVS vide
    {
        Preferences p;
        p.begin("wireguard", true);
        bool existe = p.isKey("privkey");
        p.end();
        if (!existe) {
            Serial.println("[WireGuard] Initialisation NVS avec les défauts du .conf");
            ecrireDefauts();
        }
    }

    chargerWgNVS();
    Serial.printf("[WireGuard] %s — endpoint: %s:%u  IP: %s\n",
                  wgActif ? "Activé" : "Désactivé",
                  wgEndpoint.c_str(), wgPort, wgLocalIP.c_str());
}

void gererWireGuard() {
    if (!wgActif) return;

    bool staOk = (WiFi.status() == WL_CONNECTED);

    if (staOk && !wgConnecte) {
        unsigned long maintenant = millis();
        if (tDerniereConnexion == 0 || (maintenant - tDerniereConnexion) >= WG_RETRY_MS) {
            tDerniereConnexion = maintenant;
            connecterWg();
        }
    } else if (!staOk && wgConnecte) {
        wg.end();
        wgConnecte = false;
        tDerniereConnexion = 0;
        Serial.println("[WireGuard] STA perdue — tunnel coupé");
    }
}

bool setWireGuardConfig(bool enabled, const char* privKey, const char* pubKey,
                        const char* psk, const char* endpoint, uint16_t port,
                        const char* localIP, uint16_t keepalive) {
    Preferences p;
    p.begin("wireguard", false);
    p.putBool("enabled",    enabled);
    p.putString("privkey",  privKey);
    p.putString("pubkey",   pubKey);
    p.putString("psk",      psk);
    p.putString("endpoint", endpoint);
    p.putUShort("port",     port);
    p.putString("localip",  localIP);
    p.putUShort("keepalive", keepalive);
    p.end();

    // Déconnecter le tunnel en cours si actif
    if (wgConnecte) {
        wg.end();
        wgConnecte = false;
    }
    tDerniereConnexion = 0;

    chargerWgNVS();
    Serial.printf("[WireGuard] Config mise à jour — %s\n", wgActif ? "activé" : "désactivé");
    return true;
}

void getWireGuardJson(String &out) {
    JsonDocument doc;
    doc["enabled"]   = wgActif;
    doc["connected"] = wgConnecte;
    doc["privkey"]   = wgPrivKey;
    doc["pubkey"]    = wgPubKey;
    doc["psk"]       = wgPsk;
    doc["endpoint"]  = wgEndpoint;
    doc["port"]      = wgPort;
    doc["localip"]   = wgLocalIP;
    doc["keepalive"] = wgKeepalive;
    serializeJson(doc, out);
}

#else
// --- Stubs QEMU ---
void initWireGuard()  { Serial.println("[WireGuard] Désactivé (build QEMU)"); }
void gererWireGuard() {}
void getWireGuardJson(String &out) { out = "{\"enabled\":false,\"connected\":false}"; }
bool setWireGuardConfig(bool, const char*, const char*, const char*,
                        const char*, uint16_t, const char*, uint16_t) { return false; }
#endif
