#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "FC28.h"

// === PIN & LIBRARY ===
#define PIN_SOIL 34
#define PIN_SERVO 18

LiquidCrystal_I2C lcd(0x27, 20, 4);
Servo servo;
FC28Sensor mySensor;

// === WIFI CONFIG ===
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// === LARAVEL LOCALTONET CONFIG ===
const char* laravel_sensor_url = "http://hrlbi5kne.localto.net/api/perbarui-sensor";
const char* laravel_status_url = "http://hrlbi5kne.localto.net/api/status-pompa";
const char* laravel_siram_url = "http://hrlbi5kne.localto.net/api/siram-manual";

// === QUEUE & TASK ===
typedef struct {
  float kelembapan;
  int statusPompa;
  String statusTanah;
} SensorData;

QueueHandle_t dataQueue;
TaskHandle_t TaskHTTPWorker;
TaskHandle_t TaskPompaWorker;
SemaphoreHandle_t wifiSemaphore;

// === GLOBAL HTTP CLIENT ===
HTTPClient http;
WiFiClient client;

void setup() {
  Serial.begin(115200);

  mySensor.initFC28Sensor(115200, PIN_SOIL);
  servo.attach(PIN_SERVO);

  lcd.init();
  lcd.backlight();
  lcd.setCursor(2, 0); lcd.print("Siraman Otomatis");
  lcd.setCursor(2, 1); lcd.print("Monitoring System");
  delay(2000);
  lcd.clear();

  Serial.println("Menyambungkan WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("\nāœ… WiFi Terhubung!");

  wifiSemaphore = xSemaphoreCreateMutex();
  dataQueue = xQueueCreate(10, sizeof(SensorData));

  xTaskCreatePinnedToCore(
    taskHTTPWorker,
    "Task HTTP Worker",
    32768,
    NULL,
    1,
    &TaskHTTPWorker,
    1
  );

  xTaskCreatePinnedToCore(
    taskPompaWorker,
    "Task Pompa Worker",
    32768,
    NULL,
    1,
    &TaskPompaWorker,
    1
  );
}

void loop() {
  float nilaiKelembapan = mySensor.getSoilMoisture(); // Gunakan sensor FC28
  int statusPompa = 0; // Untuk laporan, tidak kontrol servo
  String statusTanah;

  // === STATUS TANAH ===
  if (nilaiKelembapan <= 30.0) {
    statusTanah = "Sangat Kering";
  } else if (nilaiKelembapan <= 50.0) {
    statusTanah = "Kering";
  } else if (nilaiKelembapan <= 70.0) {
    statusTanah = "Lembab";
  } else if (nilaiKelembapan <= 85.0) {
    statusTanah = "Basah";
  } else {
    statusTanah = "Terlalu Basah";
  }

  // === LCD DISPLAY ===
  lcd.setCursor(0, 0); lcd.print("Status: ");
  lcd.setCursor(8, 0); lcd.print(statusTanah + "   ");
  lcd.setCursor(0, 1); lcd.print("Soil  : ");
  lcd.setCursor(8, 1); lcd.print(nilaiKelembapan, 1); lcd.print("%   ");
  lcd.setCursor(0, 2); lcd.print("Pompa : ");
  lcd.setCursor(8, 2); lcd.print("      "); // Kosongkan, akan diupdate oleh taskPompaWorker

  // === SERIAL MONITOR ===
  Serial.println("=== DATA SENSOR ===");
  Serial.printf("Kelembapan: %.1f%% | Status: %s\n", nilaiKelembapan, statusTanah.c_str());
  Serial.printf("Free heap: %d | Stack high water mark: %d\n", ESP.getFreeHeap(), uxTaskGetStackHighWaterMark(NULL));

  // === KIRIM KE QUEUE ===
  SensorData data = { nilaiKelembapan, statusPompa, statusTanah };
  xQueueSend(dataQueue, &data, portMAX_DELAY);

  delay(5000);
}

void taskHTTPWorker(void *parameter) {
  SensorData data;
  unsigned long lastLaravel = 0;

  for (;;) {
    if (xQueueReceive(dataQueue, &data, portMAX_DELAY) == pdTRUE) {
      unsigned long now = millis();
      Serial.printf("Task HTTPWorker - Stack high water mark: %d\n", uxTaskGetStackHighWaterMark(NULL));
      if (now - lastLaravel >= 5000) {
        if (xSemaphoreTake(wifiSemaphore, portMAX_DELAY) == pdTRUE) {
          kirimLaravel(data.kelembapan, data.statusPompa, data.statusTanah);
          xSemaphoreGive(wifiSemaphore);
        }
        lastLaravel = now;
      }
    }
    vTaskDelay(200 / portTICK_PERIOD_MS);
  }
}

void taskPompaWorker(void *parameter) {
  unsigned long lastCheck = 0;
  bool pompaAktif = false;
  unsigned long pompaStartTime = 0;

  for (;;) {
    unsigned long now = millis();
    float kelembapan = mySensor.getSoilMoisture(); // Gunakan sensor FC28
    Serial.printf("Task PompaWorker - Stack high water mark: %d\n", uxTaskGetStackHighWaterMark(NULL));

    // Logika otomatis berdasarkan kelembapan
    if (!pompaAktif && kelembapan < 40.0) {
      servo.write(180); // Pompa NYALA
      lcd.setCursor(8, 2); lcd.print("NYALA ");
      Serial.println("šŸ’§ Pompa dihidupkan (otomatis - kelembapan rendah)");
      pompaAktif = true;
      pompaStartTime = now;
    } else if (pompaAktif && kelembapan >= 40.0 && (now - pompaStartTime >= 5000)) {
      servo.write(0); // Pompa MATI
      lcd.setCursor(8, 2); lcd.print("MATI  ");
      Serial.println("šŸ’§ Pompa dimatikan (otomatis - kelembapan cukup)");
      pompaAktif = false;
    }

    // Cek perintah manual
    if (now - lastCheck >= 5000 && !pompaAktif) {
      String statusPompa = "";
      if (xSemaphoreTake(wifiSemaphore, portMAX_DELAY) == pdTRUE) {
        statusPompa = cekStatusPompa();
        xSemaphoreGive(wifiSemaphore);
      }
      if (statusPompa == "ON") {
        servo.write(180); // Pompa NYALA
        lcd.setCursor(8, 2); lcd.print("NYALA ");
        Serial.println("šŸ’§ Pompa dihidupkan (manual)");
        pompaAktif = true;
        pompaStartTime = now;
      }
      lastCheck = now;
    }

    // Matikan pompa setelah 5 detik untuk perintah manual
    if (pompaAktif && (now - pompaStartTime >= 5000)) {
      servo.write(0); // Pompa MATI
      lcd.setCursor(8, 2); lcd.print("MATI  ");
      Serial.println("šŸ’§ Pompa dimatikan otomatis setelah 5 detik (manual)");
      if (xSemaphoreTake(wifiSemaphore, portMAX_DELAY) == pdTRUE) {
        kirimSiramManual("OFF", "Pompa mati otomatis setelah 5 detik");
        xSemaphoreGive(wifiSemaphore);
      }
      pompaAktif = false;
    }

    vTaskDelay(200 / portTICK_PERIOD_MS);
  }
}

String cekStatusPompa() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("āŒ [Status Pompa] Tidak ada WiFi");
    return "OFF";
  }

  http.begin(client, laravel_status_url);
  http.addHeader("localtonet-skip-warning", "true");
  http.setTimeout(5000);

  String statusPompa = "OFF";
  Serial.printf("Free heap before JSON: %d\n", ESP.getFreeHeap());
  int httpCode = http.GET();
  if (httpCode == 200) {
    String response = http.getString();
    Serial.printf("āœ… [Status Pompa] %d : %s\n", httpCode, response.c_str());

    // Parse JSON response
    StaticJsonDocument<256> doc;
    DeserializationError error = deserializeJson(doc, response);
    if (!error) {
      statusPompa = doc["status_pompa"].as<String>();
    } else {
      Serial.printf("āŒ [Status Pompa] Gagal parse JSON: %s\n", error.c_str());
    }
  } else {
    Serial.printf("āŒ [Status Pompa] %d : %s\n", httpCode, http.errorToString(httpCode).c_str());
  }
  http.end();
  Serial.printf("Free heap after HTTP: %d\n", ESP.getFreeHeap());
  vTaskDelay(100 / portTICK_PERIOD_MS);
  return statusPompa;
}

void kirimSiramManual(String statusPompa, String catatan) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("āŒ [Siram Manual] Tidak ada WiFi");
    return;
  }

  String payload;
  payload.reserve(256);
  payload = "{";
  payload += "\"status_pompa\": \"" + statusPompa + "\",";
  payload += "\"catatan\": \"" + catatan + "\"";
  payload += "}";

  Serial.println("šŸš€ [Siram Manual] Payload: " + payload);
  Serial.printf("Free heap before POST: %d\n", ESP.getFreeHeap());

  http.begin(client, laravel_siram_url);
  http.addHeader("Content-Type", "application/json");
  http.addHeader("localtonet-skip-warning", "true");
  http.setTimeout(5000);

  int httpCode = http.POST(payload);
  if (httpCode > 0) {
    Serial.printf("āœ… [Siram Manual] %d : %s\n", httpCode, http.getString().c_str());
  } else {
    Serial.printf("āŒ [Siram Manual] %d : %s\n", httpCode, http.errorToString(httpCode).c_str());
  }
  http.end();
  Serial.printf("Free heap after POST: %d\n", ESP.getFreeHeap());
  vTaskDelay(100 / portTICK_PERIOD_MS);
}

void kirimLaravel(float kelembapan, int statusPompa, String statusTanah) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("āŒ [Laravel] Tidak ada WiFi");
    return;
  }

  String payload;
  payload.reserve(256);
  payload = "{";
  payload += "\"kelembapan_tanah\": " + String(kelembapan, 1) + ",";
  payload += "\"status_pompa\": \"" + String(statusPompa ? "ON" : "OFF") + "\",";
  payload += "\"status_tanah\": \"" + statusTanah + "\",";
  payload += "\"status_tombol\": \"AUTO\",";
  payload += "\"mode_siram\": \"otomatis\"}";

  Serial.println("šŸš€ [Laravel] Payload: " + payload);
  Serial.printf("Free heap before POST: %d\n", ESP.getFreeHeap());

  http.begin(client, laravel_sensor_url);
  http.addHeader("Content-Type", "application/json");
  http.addHeader("localtonet-skip-warning", "true");
  http.setTimeout(5000);

  int httpCode = http.POST(payload);
  if (httpCode > 0) {
    Serial.printf("āœ… [Laravel] %d : %s\n", httpCode, http.getString().c_str());
  } else {
    Serial.printf("āŒ [Laravel] %d : %s\n", httpCode, http.errorToString(httpCode).c_str());
  }
  http.end();
  Serial.printf("Free heap after POST: %d\n", ESP.getFreeHeap());
  vTaskDelay(100 / portTICK_PERIOD_MS);
}