#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include "time.h"

// Cấu hình Wi-Fi & Device
const char* ssid       = "XM";
const char* password   = "79797979";
const char* device_id  = "ESP32_PUMP_01";

// Cấu hình endpoints API HTTPS 
const char* get_schedule_url = "https://hivemq.nongnghiep24h.com/api.php?device_id=ESP32_PUMP_01";
const char* post_log_url     = "https://hivemq.nongnghiep24h.com/api.php";

// Định nghĩa mảng chân GPIO nối với 8 Relay (Thay đổi tùy cấu hình thực tế)
const int relayPins[8] = {13, 12, 14, 27, 26, 25, 33, 32}; 

// Cấu hình mã hóa SSL Root CA Certificate của Server (Ví dụ cho Let's Encrypt ISRG Root X1)
// Bạn cần lấy mã Root CA chính xác của hosting/server đang chạy PHP của bạn
const char* root_ca = "-----BEGIN CERTIFICATE-----\n"
"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\n"
"-----END CERTIFICATE-----";

// Khởi tạo các phân vùng thư viện
Preferences preferences;
WiFiClientSecure client;

// Mảng chuyển đổi thứ trong tuần từ struct tm sang định dạng chuỗi mẫu của bạn
const char* daysOfWeekNames[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

unsigned long lastScheduleCheck = 0;
const unsigned long checkInterval = 60000; // Cập nhật lại lịch trình từ máy chủ mỗi 5 phút

void setup() {
  Serial.begin(115200);
  
  // Thiết lập các chân Relay làm Output đầu ra
  for (int i = 0; i < 8; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], LOW); // Trạng thái mặc định ban đầu là tắt (LOW)
  }

  // Kết nối Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected!");

  // Cấu hình đồng bộ thời gian thực thông qua Internet NTP
  configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov"); // Múi giờ GMT+7 (Việt Nam)
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Lỗi! Không thể đồng bộ thời gian từ NTP Server");
  }

  // Cấu hình chứng chỉ bảo mật SSL Root CA cho Client
  client.setCACert(root_ca);

  // Tải danh sách lịch trình từ Server về lưu cục bộ ngay khi khởi động
  fetchAndSaveSchedules();
}

void loop() {
  checkAndExecuteSchedules();
  
  // Tự động kiểm tra cập nhật thay đổi cấu hình lịch trình định kỳ từ Web Server
  if (millis() - lastScheduleCheck > checkInterval) {
    fetchAndSaveSchedules();
    lastScheduleCheck = millis();
  }
  delay(1000); // Vòng lặp quét điều kiện mỗi 1 giây
}

// Hàm tải dữ liệu lịch trình dạng JSON qua HTTPS và lưu vào bộ nhớ Flash Preferences
void fetchAndSaveSchedules() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    if (http.begin(client, get_schedule_url)) {
      int httpCode = http.GET();
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        Serial.println("Tải lịch trình thành công: " + payload);

        // Khởi tạo Preferences lưu trữ dữ liệu bền vững
        preferences.begin("sched_storage", false);
        preferences.putString("json_data", payload); // Lưu chuỗi JSON thô vào bộ nhớ Flash
        preferences.end();
      } else {
        Serial.printf("[HTTPS] Lỗi GET từ Server, mã lỗi: %s\n", http.errorToString(httpCode).c_str());
      }
      http.end();
    }
  }
}

// Hàm kiểm tra thời gian thực với lịch trình đã lưu để thực thi đóng ngắt relay
void checkAndExecuteSchedules() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) return;

  // Lấy ra chuỗi định dạng thời gian thực tại: "HH:MM" và "Thứ"
  char currentTime[6];
  strftime(currentTime, sizeof(currentTime), "%H:%M", &timeinfo);
  String currentDayStr = daysOfWeekNames[timeinfo.tm_wday];

  // Đọc dữ liệu JSON cấu hình lịch trình từ Flash Preferences lên
  preferences.begin("sched_storage", true);
  String cachedJSON = preferences.getString("json_data", "[]");
  preferences.end();

  // Phân tích cú pháp JSON bằng thư viện ArduinoJson
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, cachedJSON);
  if (error) return;

  JsonArray schedList = doc.as<JsonArray>();
  for (JsonObject sched : schedList) {
    int relay_id = sched["relay_id"];
    const char* target_time = sched["target_time"]; // Định dạng "HH:MM"
    JsonArray days = sched["days_of_week"].as<JsonArray>();
    int action = sched["action"];

    // Xác định xem lịch trình có khớp hoàn toàn với Thời gian + Ngày hiện tại hay không
    if (strcmp(currentTime, target_time) == 0) {
      bool dayMatches = false;
      for (JsonVariant day : days) {
        if (day.as<String>() == currentDayStr) {
          dayMatches = true;
          break;
        }
      }

      if (dayMatches) {
        int pinIndex = relay_id - 1; // ID Relay chạy từ 1-8 ứng với chỉ số mảng 0-7
        if (pinIndex >= 0 && pinIndex < 8) {
          int currentPinState = digitalRead(relayPins[pinIndex]);
          
          // Tránh ghi lặp lại trạng thái liên tục trong cùng một phút
          if (currentPinState != action) {
            digitalWrite(relayPins[pinIndex], action);
            Serial.printf("Kích hoạt Relay %d -> Trạng thái: %d\n", relay_id, action);
            
            // Gửi dữ liệu nhật ký sự kiện này về Server qua cổng HTTPS POST
            sendLogToServer(relay_id, action, "SUCCESS");
          }
        }
      }
    }
  }
}

// Hàm đẩy log trạng thái thiết bị lên bảng dữ liệu MySQL thông qua API JSON HTTPS POST
void sendLogToServer(int relay_id, int action, const char* status) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    if (http.begin(client, post_log_url)) {
      http.addHeader("Content-Type", "application/json");

      // Khởi tạo chuỗi Json gửi đi
      JsonDocument doc;
      doc["device_id"] = device_id;
      doc["relay_id"]  = relay_id;
      doc["action"]    = action;
      doc["status"]    = status;

      String requestBody;
      serializeJson(doc, requestBody);

      int httpResponseCode = http.POST(requestBody);
      if (httpResponseCode > 0) {
        Serial.printf("Gửi Log thành công, mã phản hồi: %d\n", httpResponseCode);
      } else {
        Serial.printf("Lỗi gửi Log hệ thống: %s\n", http.errorToString(httpResponseCode).c_str());
      }
      http.end();
    }
  }
}
