// ============================================================================
// peer_relay.cpp — see peer_relay.h for design notes.
// ============================================================================

#include "peer_relay.h"
#include "mesh_publish.h"

#ifdef ARDUINO

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <nvs.h>
#include <cstring>

static const char* TAG = "PeerRelay";

#define PEER_NVS_NS    "pdl_peer"
#define PEER_NVS_HOST  "host"
#define PEER_NVS_PORT  "port"
#define PEER_HTTP_TIMEOUT_MS 4000

static char           s_host[64] = {0};
static uint16_t       s_port     = 80;
static PeerRelayStats s_stats{};

void peer_relay_init() {
    nvs_handle_t h;
    if (nvs_open(PEER_NVS_NS, NVS_READONLY, &h) != ESP_OK) return;
    size_t len = sizeof(s_host);
    if (nvs_get_str(h, PEER_NVS_HOST, s_host, &len) != ESP_OK) s_host[0] = '\0';
    uint16_t p = 0;
    if (nvs_get_u16(h, PEER_NVS_PORT, &p) == ESP_OK && p != 0) s_port = p;
    nvs_close(h);

    if (s_host[0]) {
        Serial.printf("[%s] Peer relay restored: http://%s:%u%s\n",
                      TAG, s_host, (unsigned)s_port, PEER_RELAY_PATH);
    }
}

void peer_relay_configure(const char* host, uint16_t port) {
    if (host == nullptr) host = "";
    strncpy(s_host, host, sizeof(s_host) - 1);
    s_host[sizeof(s_host) - 1] = '\0';
    if (port != 0) s_port = port;

    nvs_handle_t h;
    if (nvs_open(PEER_NVS_NS, NVS_READWRITE, &h) == ESP_OK) {
        nvs_set_str(h, PEER_NVS_HOST, s_host);
        nvs_set_u16(h, PEER_NVS_PORT, s_port);
        nvs_commit(h);
        nvs_close(h);
    }

    if (s_host[0]) {
        Serial.printf("[%s] Configured: http://%s:%u%s\n",
                      TAG, s_host, (unsigned)s_port, PEER_RELAY_PATH);
    } else {
        Serial.printf("[%s] Peer relay disabled.\n", TAG);
    }
}

bool peer_relay_is_configured() { return s_host[0] != '\0'; }

bool peer_relay_post_detection(const DetectionEvent& evt) {
    if (!s_host[0]) { s_stats.skipped_unconfigured++; return false; }
    if (WiFi.status() != WL_CONNECTED) { s_stats.forwarded_failed++; return false; }

    // Send the EXACT 33-byte wire format the LoRa path uses, hex-encoded.
    // Reusing mesh_serialize_event() rather than inventing a JSON shape means
    // the receiving side can run the identical deserialize + validate path,
    // so a packet forwarded over WiFi and one heard over LoRa are
    // indistinguishable downstream — including the mesh_event_is_plausible()
    // gate that rejects foreign traffic.
    uint8_t packet[DETECTION_PACKET_SIZE];
    size_t len = mesh_serialize_event(evt, packet, sizeof(packet));
    if (len == 0) { s_stats.forwarded_failed++; return false; }

    char hex[(DETECTION_PACKET_SIZE * 2) + 1];
    static const char* kHex = "0123456789ABCDEF";
    for (size_t i = 0; i < len; i++) {
        hex[i * 2]     = kHex[(packet[i] >> 4) & 0x0F];
        hex[i * 2 + 1] = kHex[packet[i] & 0x0F];
    }
    hex[len * 2] = '\0';

    char url[128];
    snprintf(url, sizeof(url), "http://%s:%u%s", s_host, (unsigned)s_port, PEER_RELAY_PATH);

    WiFiClient client;
    HTTPClient http;
    if (!http.begin(client, url)) {
        s_stats.forwarded_failed++;
        Serial.printf("[%s] begin() failed for %s\n", TAG, url);
        return false;
    }
    http.setTimeout(PEER_HTTP_TIMEOUT_MS);
    http.addHeader("Content-Type", "text/plain");

    int code = http.POST((uint8_t*)hex, len * 2);
    http.end();

    if (code >= 200 && code < 300) {
        s_stats.forwarded_ok++;
        Serial.printf("[%s] Forwarded detection to %s (HTTP %d)\n", TAG, s_host, code);
        return true;
    }

    s_stats.forwarded_failed++;
    Serial.printf("[%s] Forward FAILED to %s (HTTP %d) — LoRa path still applies\n",
                  TAG, s_host, code);
    return false;
}

void peer_relay_get_stats(PeerRelayStats* out) { if (out) *out = s_stats; }

#else  // !ARDUINO — native/test stub

void peer_relay_init() {}
void peer_relay_configure(const char*, uint16_t) {}
bool peer_relay_is_configured() { return false; }
bool peer_relay_post_detection(const DetectionEvent&) { return false; }
void peer_relay_get_stats(PeerRelayStats* out) { if (out) *out = PeerRelayStats{}; }

#endif
