/*
  ESP32 Bi-Directional Web Server & Telemetry Client
  Board: ESP32 Dev Module
  Dependencies: ArduinoJson (v6+), WiFi, HTTPClient, ESPAsyncWebServer (optional embedded server)
*/

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <DHT.h>

// WiFi Configuration
const char* ssid = "CLARO1_DCFBBC";
const char* password = "2246gsPwpS";

// Laravel TALL Stack Server Endpoint
const char* serverUrl = "http://indelrue.com/api/esp32/telemetry";
const char* ackUrl    = "http://indelrue.com/api/esp32/commands/ack";
const char* deviceId  = "ESP32_LOOM_01";

// Hardware Pins
#define RELAY_1_PIN 18
#define RELAY_2_PIN 19
#define DHTPIN 4
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);

// Execution State
unsigned long lastSendTime = 0;
unsigned long telemetryInterval = 5000; // 5 seconds interval

// Function Prototypes
void sendTelemetryAndPollCommands();
void processCommand(int cmdId, const char* commandStr, const char* payloadStr);
void sendAck(int cmdId, const char* statusStr);


void setup() {
  Serial.begin(115200);
  
  pinMode(RELAY_1_PIN, OUTPUT);
  pinMode(RELAY_2_PIN, OUTPUT);
  digitalWrite(RELAY_1_PIN, LOW);
  digitalWrite(RELAY_2_PIN, LOW);

  dht.begin();

  // Connect WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected. IP: " + WiFi.localIP().toString());
}

void loop() {
  if (millis() - lastSendTime > telemetryInterval) {
    lastSendTime = millis();
    sendTelemetryAndPollCommands();
  }
}

void sendTelemetryAndPollCommands() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi Disconnected!");
    return;
  }

  HTTPClient http;
  http.begin(serverUrl);
  http.addHeader("Content-Type", "application/json");

  // Read sensors
  float temp = dht.readTemperature();
  float hum = dht.readHumidity();
  if (isnan(temp)) temp = 24.5; // fallback test value
  if (isnan(hum)) hum = 55.0;

  bool r1 = digitalRead(RELAY_1_PIN) == HIGH;
  bool r2 = digitalRead(RELAY_2_PIN) == HIGH;
  int rssi = WiFi.RSSI();

  // Build JSON
  StaticJsonDocument<300> doc;
  doc["device_id"] = deviceId;
  doc["ip_address"] = WiFi.localIP().toString();
  doc["temperature"] = temp;
  doc["humidity"] = hum;
  doc["relay_1"] = r1;
  doc["relay_2"] = r2;
  doc["rssi"] = rssi;
  doc["status"] = "online";

  String jsonPayload;
  serializeJson(doc, jsonPayload);

  int httpCode = http.POST(jsonPayload);

  if (httpCode > 0) {
    String response = http.getString();
    Serial.println("HTTP Response Code: " + String(httpCode));
    
    // Parse response for pending commands
    StaticJsonDocument<1024> respDoc;
    DeserializationError err = deserializeJson(respDoc, response);
    if (!err && respDoc["success"] == true) {
      JsonArray commands = respDoc["commands"].as<JsonArray>();
      for (JsonObject cmd : commands) {
        int cmdId = cmd["id"];
        const char* commandStr = cmd["command"];
        const char* payloadStr = cmd["payload"];

        processCommand(cmdId, commandStr, payloadStr);
      }
    }
  } else {
    Serial.println("Error on HTTP request: " + String(httpCode));
  }
  http.end();
}

void processCommand(int cmdId, const char* commandStr, const char* payloadStr) {
  Serial.print("Processing Command #");
  Serial.print(cmdId);
  Serial.print(": ");
  Serial.println(commandStr);

  bool success = false;
  String cmd = String(commandStr);
  String payload = String(payloadStr);

  if (cmd == "toggle_relay_1") {
    digitalWrite(RELAY_1_PIN, payload == "ON" ? HIGH : LOW);
    success = true;
  } else if (cmd == "toggle_relay_2") {
    digitalWrite(RELAY_2_PIN, payload == "ON" ? HIGH : LOW);
    success = true;
  } else if (cmd == "reboot") {
    sendAck(cmdId, "executed");
    delay(500);
    ESP.restart();
  } else if (cmd == "set_interval") {
    telemetryInterval = payload.toInt();
    if (telemetryInterval < 1000) telemetryInterval = 5000;
    success = true;
  }

  sendAck(cmdId, success ? "executed" : "failed");
}

void sendAck(int cmdId, const char* statusStr) {
  HTTPClient http;
  http.begin(ackUrl);
  http.addHeader("Content-Type", "application/json");

  StaticJsonDocument<128> doc;
  doc["command_id"] = cmdId;
  doc["status"] = statusStr;

  String jsonPayload;
  serializeJson(doc, jsonPayload);

  http.POST(jsonPayload);
  http.end();
}
