#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <HTTPClient.h>
#include <ESP32Servo.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

#define WIFI_SSID     "Hidden Network"
#define WIFI_PASSWORD "tanyapakkost"
#define API_KEY       "AIzaSyAHroHSc9Sp2lcZMf7SrCaV9cBjbjlxcfQ"
#define DATABASE_URL  "https://smartinfuse-49274-default-rtdb.firebaseio.com/"
#define LEGACY_TOKEN  "EQUbBn1dXMb2JQUW9UbMtpNKm2wP8VmcOi3tJDLu"
#define ROOM_NAME     "ruang_kamboja"
#define DEVICE_ID     "INF-ICU-127"

#define SENSOR_PIN  34
#define SERVO_PIN   21
#define BUZZER_PIN  18
#define BUTTON_PIN  19
#define LED_PIN     22

#define FB_BASE "/realtime/rooms/" ROOM_NAME "/devices/" DEVICE_ID
#define FB_CTRL FB_BASE "/controlling"
#define FB_MON  FB_BASE "/monitoring"

#define SERVO_SWEEP_DELAY 12

// ==== KONFIGURASI KALIBRASI SENSOR TETES (BARU) ====
const int jumlahSampelBaca      = 20;   // averaging tiap pembacaan
const int jumlahSampelKalibrasi = 100;  // jumlah sampel saat fase kalibrasi awal
const unsigned long debounceDelayTetes = 300; // ms, jarak minimum antar tetesan

float baseline  = 0;
float noiseMax  = 0;
int   threshold = 0;

int stateSebelumnyaTetes = LOW;
unsigned long waktuTerakhirTetes = 0;
// ======================================================

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
Servo valveServo;

volatile int  dropCount = 0;
volatile bool newDrop   = false;

unsigned long lastDropTime    = 0;
unsigned long windowStart     = 0;
unsigned long lastFbCheck     = 0;
unsigned long lastClickTime   = 0;
unsigned long buttonPressTime = 0;

bool buzzerActive    = false;
bool servoOpen       = false;
int  servoCurrentPos = 0;
int  clickCount      = 0;

bool fbReady() {
  return WiFi.status() == WL_CONNECTED && Firebase.ready();
}

// ==== FUNGSI KALIBRASI & PEMBACAAN SENSOR TETES (BARU) ====
int bacaRataRata() {
  long total = 0;
  for (int i = 0; i < jumlahSampelBaca; i++) {
    total += analogRead(SENSOR_PIN);
    delay(1);
  }
  return total / jumlahSampelBaca;
}

void kalibrasiOtomatis() {
  Serial.println("=== KALIBRASI OTOMATIS DIMULAI ===");
  Serial.println("Pastikan TIDAK ADA TETESAN selama proses ini...");
  delay(2000);

  long total = 0;
  int nilaiMin = 4095;
  int nilaiMax = 0;

  for (int i = 0; i < jumlahSampelKalibrasi; i++) {
    int nilai = bacaRataRata();
    total += nilai;
    if (nilai < nilaiMin) nilaiMin = nilai;
    if (nilai > nilaiMax) nilaiMax = nilai;
    delay(50);
  }

  baseline = (float)total / jumlahSampelKalibrasi;
  noiseMax = nilaiMax - baseline;
  threshold = baseline + (noiseMax * 3) + 20;

  Serial.println("=== KALIBRASI SELESAI ===");
  Serial.print("Baseline: "); Serial.println(baseline);
  Serial.print("Noise range (min-max saat kalibrasi): ");
  Serial.print(nilaiMin); Serial.print(" - "); Serial.println(nilaiMax);
  Serial.print("Threshold terpakai: "); Serial.println(threshold);
  Serial.println("=== MULAI MODE DETEKSI TETESAN ===");
}

// Dipanggil tiap loop, menggantikan mekanisme interrupt lama.
// Mengisi dropCount & newDrop persis seperti sebelumnya, supaya
// bagian kode lain (Firebase, servo, dll) tidak perlu diubah.
void cekTetesan() {
  int nilai = bacaRataRata();
  int stateSekarang = (nilai > threshold) ? HIGH : LOW;

  if (stateSekarang == HIGH && stateSebelumnyaTetes == LOW) {
    if (millis() - waktuTerakhirTetes > debounceDelayTetes) {
      dropCount++;
      newDrop = true;
      waktuTerakhirTetes = millis();
      Serial.print("[TETES] ke-");
      Serial.print(dropCount);
      Serial.print(" | Nilai: ");
      Serial.println(nilai);
    }
  }
  stateSebelumnyaTetes = stateSekarang;
}
// ============================================================

void setupFirebase() {
  config.api_key        = API_KEY;
  config.database_url   = DATABASE_URL;
  config.signer.tokens.legacy_token = LEGACY_TOKEN;
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
}

void activateBuzzer() {
  if (buzzerActive) return;
  digitalWrite(BUZZER_PIN, HIGH);
  buzzerActive = true;
}

void stopBuzzer() {
  if (!buzzerActive) return;
  digitalWrite(BUZZER_PIN, LOW);
  buzzerActive = false;
}

void sweepServo(int from, int to) {
  Serial.printf("[SWEEP] %d → %d\n", from, to);
  int step = (to > from) ? 1 : -1;
  for (int pos = from; pos != to; pos += step) {
    valveServo.write(pos);
    delay(SERVO_SWEEP_DELAY);
  }
  valveServo.write(to);
  servoCurrentPos = to;
  delay(200);
}

void openValve() {
  if (servoOpen) return;
  Serial.println("[SERVO] Membuka katup → 140°");
  sweepServo(servoCurrentPos, 140);
  servoOpen = true;
  Serial.println("[SERVO] Katup terbuka");
}

void closeValve() {
  if (!servoOpen) return;
  Serial.println("[SERVO] Menutup katup → 0°");
  sweepServo(servoCurrentPos, 0);
  servoOpen = false;
  Serial.println("[SERVO] Katup tertutup");
}

void kirimAPI(int dpm) {
  if (!fbReady()) return;
  HTTPClient http;
  http.begin("https://smart-infus-service.vercel.app/api/kirimdata");
  http.addHeader("Content-Type", "application/json");
  String body = "{\"device_id\":\"" DEVICE_ID "\",\"drop_per_menit\":" + String(dpm) + "}";
  int httpCode = http.POST(body);
  Serial.printf("[API] HTTP %d\n", httpCode);
  http.end();
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

  Serial.begin(115200);
  delay(500);

  Serial.printf("[BOOT] Reset reason: %d\n", esp_reset_reason());

  pinMode(SENSOR_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN,    OUTPUT);

  digitalWrite(BUZZER_PIN, LOW);

  // NOTE: attachInterrupt lama untuk SENSOR_PIN dihapus,
  // karena deteksi tetesan sekarang berbasis analogRead + threshold
  // hasil kalibrasi otomatis (dipanggil lewat cekTetesan() di loop()).

  valveServo.attach(SERVO_PIN, 500, 2400);
  delay(200);
  valveServo.write(0);
  servoCurrentPos = 0;
  delay(600);
  servoOpen = false;
  Serial.println("[SERVO] Init selesai → posisi 0°");

  Serial.print("Connecting WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.printf("\nIP: %s\n", WiFi.localIP().toString().c_str());

  setupFirebase();

  Serial.print("Waiting Firebase");
  while (!Firebase.ready()) { delay(300); Serial.print("."); }
  Serial.println("\nFirebase ready");

  Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false);
  Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open",    false);

  digitalWrite(LED_PIN, HIGH);
  windowStart  = millis();
  lastDropTime = millis();
  lastFbCheck  = millis();

  // Kalibrasi otomatis sensor tetes (BARU) - pastikan tidak ada
  // tetesan/aliran saat proses ini berjalan.
  kalibrasiOtomatis();

  Serial.println("READY");
}

void loop() {

  // Polling sensor tetes (BARU) - menggantikan interrupt lama
  cekTetesan();

  if (newDrop) {
    newDrop      = false;
    lastDropTime = millis();
  }

  if (millis() - windowStart >= 60000UL) {
    portDISABLE_INTERRUPTS();
    int dpm   = dropCount;
    dropCount = 0;
    portENABLE_INTERRUPTS();
    windowStart = millis();

    Serial.printf("[MONITOR] drop/menit = %d\n", dpm);
    if (fbReady()) {
      Firebase.RTDB.setInt(&fbdo, FB_MON "/drop_rate", dpm);
      kirimAPI(dpm);
      if (dpm == 0) {
        Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open",    false);
        Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", true);
      }
    }
  }

  if (millis() - lastFbCheck >= 2000UL) {
    lastFbCheck = millis();

    if (fbReady()) {
      if (Firebase.RTDB.getBool(&fbdo, FB_CTRL "/servo_open")) {
        bool fb = fbdo.boolData();
        Serial.printf("[FB] servo_open = %s | state = %s\n",
                      fb ? "true" : "false", servoOpen ? "OPEN" : "CLOSED");
        if (fb  && !servoOpen) openValve();
        if (!fb &&  servoOpen) closeValve();
      }

      if (Firebase.RTDB.getBool(&fbdo, FB_CTRL "/buzzer_active")) {
        bool fb = fbdo.boolData();
        if (fb  && !buzzerActive) activateBuzzer();
        if (!fb &&  buzzerActive) stopBuzzer();
      }
    }
  }

  if (digitalRead(BUTTON_PIN) == LOW) {
    buttonPressTime = millis();
    delay(50);
    while (digitalRead(BUTTON_PIN) == LOW);
    unsigned long dur = millis() - buttonPressTime;

    if (dur > 2000) {
      Serial.println("[BTN] Hold → matiin buzzer");
      if (fbReady()) Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false);
      clickCount = 0;
    } else {
      if (clickCount < 2) {
        clickCount++;
        lastClickTime = millis();
        Serial.printf("[BTN] Klik ke-%d\n", clickCount);
      }
    }
  }

  if (clickCount > 0 && millis() - lastClickTime > 500UL) {
    if (fbReady()) {
      if (clickCount == 1) {
        Serial.println("[BTN] 1 klik → buka servo + matiin buzzer");
        Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open",    true);
        Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false); // ← PERUBAHAN
      }
      if (clickCount == 2) {
        Serial.println("[BTN] 2 klik → tutup servo");
        Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open", false);
      }
    }
    clickCount = 0;
  }

  delay(10);
}