#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include "time.h"

// --- Configuration ---
const char* ssid     = "XM";
const char* password = "79797979";
const char* device_id = "ESP32_MAIN";

// API Endpoints
const char* fetch_api_url = "https://hivemq.nongnghiep24h.com/get_schedules.php?device_id=ESP32_MAIN";
const char* log_api_url   = "https://hivemq.nongnghiep24h.com/log_status.php";

// NTP Server Settings
const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 25200; // Adjust for your timezone (e.g., +7 hours = 25200)
const int   daylightOffset_sec = 0;

// Hardware Pins (5 Relays)
const int relayPins[5] = {13, 12, 14, 27, 26};
bool relayStates[5] = {false, false, false, false, false};

// Instances
Preferences preferences;

// Day mapping array
const char* daysOfWeekNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

void setup() {
  Serial.begin(115200);
  
  // Initialize Relay Pins
  for(int i=0; i<5; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], LOW); // Default OFF
  }

  // Connect WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected");

  // Sync Time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  
  // Initialize Preferences (Storage)
  preferences.begin("relay_sched", false);
  
  // Fetch initial schedule from API
  syncSchedulesWithAPI();
}

void loop() {
  static unsigned long lastCheck = 0;
  // Check every 30 seconds for schedule match
  if (millis() - lastCheck > 30000) { 
    lastCheck = millis();
    checkSchedules();
  }
  syncSchedulesWithAPI();
}

void syncSchedulesWithAPI() {
  if (WiFi.status() != WL_CONNECTED) return;

  HTTPClient http;
  http.begin(fetch_api_url);
  int httpCode = http.GET();

  if (httpCode == HTTP_CODE_OK) {
    String payload = http.getString();
    
    // Save plain text JSON response directly to Flash Memory
    preferences.putString("json_data", payload);
    Serial.println("Schedules updated and saved to flash memory.");
  }
  http.end();
}

void checkSchedules() {
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Format current time variants
  char curTime[9]; // HH:MM:SS
  strftime(curTime, sizeof(curTime), "%H:%M:%S", &timeinfo);
  String curDay = daysOfWeekNames[timeinfo.tm_wday];

  // Read schedules from Flash
  String jsonPayload = preferences.getString("json_data", "[]");
  
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, jsonPayload);
  if (error) return;

  JsonArray array = doc.as<JsonArray>();
  for(JsonObject obj : array) {
    int relay_id = obj["relay_id"]; // 1 to 5
    String target_time = obj["target_time"]; // "08:30:00"
    int action = obj["action"]; // 1 or 0
    const char* days = obj["days_of_week"];

    // Format check (Only process if current time matches HH:MM)
    if (target_time.substring(0,5) == String(curTime).substring(0,5)) {
      
      // Parse days of week array from inside the object
      JsonDocument dayDoc;
      deserializeJson(dayDoc, days);
      JsonArray daysArray = dayDoc.as<JsonArray>();
      
      for(JsonVariant v : daysArray) {
        if (v.as<String>() == curDay) {
          executeRelayAction(relay_id - 1, action);
        }
      }
    }
  }
}

void executeRelayAction(int index, int action) {
  if (index < 0 || index > 4) return;
  
  bool newState = (action == 1);
  if (relayStates[index] != newState) {
    relayStates[index] = newState;
    digitalWrite(relayPins[index], newState ? HIGH : LOW);
    
    Serial.printf("Relay %d changed to %s\n", index + 1, newState ? "ON" : "OFF");
    sendLogToAPI(index + 1, action);
  }
}

void sendLogToAPI(int relay_id, int status) {
  if (WiFi.status() != WL_CONNECTED) return;

  HTTPClient http;
  http.begin(log_api_url);
  http.addHeader("Content-Type", "application/json");

  JsonDocument doc;
  doc["device_id"] = device_id;
  doc["relay_id"] = relay_id;
  doc["status"] = status;

  String requestBody;
  serializeJson(doc, requestBody);

  int httpResponseCode = http.POST(requestBody);
  http.end();
}
