#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Firebase_ESP_Client.h>
#include <WiFiManager.h>
#include <vector>
#include <map>

#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"

#define FIREBASE_PROJECT_ID "ta-running"
#define API_KEY "AIzaSyDcIQatv2KWTh96025wlFNEbH3xH2MK88k"
#define DATABASE_URL "https://ta-running-default-rtdb.asia-southeast1.firebasedatabase.app/"

#define USER_EMAIL "esp32@running.com"
#define USER_PASSWORD "35P32RF1D"

#define SS_PIN 5
#define RST_PIN 2
#define BUZZER_PIN 4
#define BUTTON_REG_PIN 26
#define BUTTON_START_PIN 27

LiquidCrystal_I2C lcd(0x27, 16, 2);
MFRC522 mfrc522(SS_PIN, RST_PIN);

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

String userUID = "";
String getUID();
void buzz(int durasi);
bool modeDaftarRFID = false;
unsigned long regStartTime = 0;
const unsigned long regTimeout = 10000; // 10 detik timeout registrasi

bool modeScanUlang = false;
unsigned long waktuMulaiScanUlang = 0;
const unsigned long batasWaktuScanUlang = 60000; // 60 detik (kembali ke 60s untuk individual)

bool pesanScanAktif = false;
unsigned long waktuPesanScanAktif = 0;

bool pesanTungguUser = false;
unsigned long waktuPesanTungguUser = 0;

int totalParticipantsInEvent = 0; // Untuk melacak total peserta yang terdaftar di event ini
int finishedParticipantsCount = 0; // Untuk melacak berapa banyak peserta yang sudah menyelesaikan lapnya

// =====================================================================================
// STRUCT DAN VARIABEL UNTUK MODE INDIVIDUAL (LOGIKA LAMA ANDA)
// =====================================================================================
struct RunningUserIndividual { // Diubah namanya agar tidak bentrok
  String uidTag, uidApk, type;
  int putaranTarget, scanCount;
  unsigned long startTime, lastScanTime;
  bool finished;
};

struct PendingUserIndividual { // Diubah namanya agar tidak bentrok
  String uidTag, uidApk, type;
  int putaranTarget;
  bool readyToStart;
  unsigned long waktuMasuk;
};

std::vector<RunningUserIndividual> runningUsersIndividual; // Diubah namanya
std::vector<PendingUserIndividual> pendingUsersIndividual; // Diubah namanya
std::map<String, String> cachedUIDsIndividual; // Diubah namanya

// =====================================================================================
// STRUCT DAN VARIABEL UNTUK MODE EVENT (LOGIKA BARU)
// =====================================================================================
struct EventParticipant {
  String uidTag;
  int lapCount; // Jumlah lap yang sudah dicatat untuk peserta ini (termasuk lap_0)
  unsigned long lapStartTime; // Waktu lap_0 dicatat (millis())
  unsigned long lastScanTime; // Waktu scan terakhir (millis())
  int putaranTarget; // Target putaran untuk peserta ini (diambil dari /activities jika ada)
  bool finished; // Status selesai untuk peserta ini
};
std::map<String, EventParticipant> eventParticipants; // Map UID Tag ke EventParticipant

String currentEventId = ""; // ID event yang sedang aktif
unsigned long eventGlobalStartTime = 0; // Waktu event global dimulai
bool countdownActive = false; // Flag untuk countdown scan
bool eventStarted = false; // Flag event global sudah dimulai
bool waitingForGlobalStartButton = false; // Flag menunggu tombol START event global

unsigned long waktuMulaiScan = 0; // Untuk countdown dan timeout registrasi individual

// =====================================================================================
// VARIABEL UMUM
// =====================================================================================
unsigned long lastRFIDScanTime = 0;
const unsigned long rfidScanInterval = 200;

unsigned long lastCheck = 0;
const unsigned long checkInterval = 5000;

//button start (untuk mode individual)
bool lastStartState = HIGH;
int stableStartState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30; // Delay debounce (ms)
bool waitingForRelease = false;

struct DeleteSchedule {
  String uidTag;
  unsigned long deleteTime; // waktu millis() untuk hapus
};
std::vector<DeleteSchedule> deleteSchedules;


void setup() {
  Serial.begin(115200);
  lcd.init(); lcd.backlight();

  WiFiManager wm;
  wm.autoConnect("RunIoT_Setup", "12345678");
  lcd.clear(); lcd.print("WiFi Connected"); delay(1000);

  //Sinkronisasi waktu dari NTP
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("❌ Gagal sinkron waktu NTP");
  } else {
    Serial.println("✅ Waktu sinkron dari NTP");
  }

  //Setup Firebase
  config.api_key = API_KEY;
  config.database_url = DATABASE_URL;
  auth.user.email = USER_EMAIL;
  auth.user.password = USER_PASSWORD;
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
  lcd.clear(); lcd.print("Login Firebase"); delay(1000);

  while ((auth.token.uid) == "") {
    Firebase.refreshToken(&config);
    delay(500);
  }
  userUID = String(auth.token.uid.c_str());

  SPI.begin(); mfrc522.PCD_Init();
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(BUTTON_REG_PIN, INPUT_PULLUP);
  pinMode(BUTTON_START_PIN, INPUT_PULLUP);
  digitalWrite(BUZZER_PIN, LOW);
  lcd.clear(); lcd.print("Sistem Ready");
}

// =====================================================================================
// DEKLARASI FUNGSI
// =====================================================================================
// Fungsi Umum
void buzz(int durasi);
String getUID();
String getISOTime();
void resetSystemState(); // Untuk event
void checkDeleteSchedules(); // Untuk kedua mode

// Fungsi Mode Individual (Logika Asli Anda)
void handleRegistrationIndividual(); // Diubah namanya
void cekTombolStartIndividual(); // Diubah namanya
void cekFirebaseIndividual(); // Diubah namanya
void cekTimeoutScanUlangIndividual(); // Diubah namanya
void handleRFIDScanIndividual(); // Diubah namanya
void updateRunningUsersIndividual(); // Diubah namanya

// Fungsi Mode Event (Logika Baru)
void listenToEventStatus();
void startScanCountdownLogic();
void handleStartButtonEvent(); // Untuk tombol START event global & lap_0
void handleRFIDScanEvent(); // Untuk scan RFID saat event berjalan
void updateEventParticipants(); // Untuk mengelola eventParticipants map
void checkEventCompletion();

void loop() {
  if (!Firebase.ready()) return;

  // Cek status event di Firebase (dari aplikasi superuser)
  listenToEventStatus();

  // =====================================================================================
  // LOGIKA UTAMA LOOP BERDASARKAN MODE
  // =====================================================================================
  if (eventStarted || countdownActive || waitingForGlobalStartButton) { // MODE EVENT
    // Handle tombol START untuk event (global start / lap_0)
    handleStartButtonEvent();

    if (countdownActive) {
      startScanCountdownLogic();
    } else if (waitingForGlobalStartButton) {
      lcd.setCursor(0, 0);
      lcd.print("Tekan START Event");
      lcd.setCursor(0, 1);
      lcd.print("                ");
    } else if (eventStarted) {
      // Tampilkan waktu berjalan event global
      unsigned long elapsed = millis() - eventGlobalStartTime;
      int sec = (elapsed / 1000) % 60;
      int min = (elapsed / 60000) % 60;
      int hr = (elapsed / 3600000);

      lcd.setCursor(0, 0);
      lcd.print("Event: ");
      if (hr < 10) lcd.print("0");
      lcd.print(hr);
      lcd.print(":");
      if (min < 10) lcd.print("0");
      lcd.print(min);
      lcd.print(":");
      if (sec < 10) lcd.print("0");
      lcd.print(sec);
      lcd.print(" ");
    }
    
    // Handle RFID Scan untuk event
    handleRFIDScanEvent();
    // Update participant status (misal finished)
    updateEventParticipants();
    checkEventCompletion();

  } else { // MODE INDIVIDUAL (jika tidak ada event aktif)
    handleRFIDScanIndividual();
    cekTombolStartIndividual();
    handleRegistrationIndividual();
    cekFirebaseIndividual();
    cekTimeoutScanUlangIndividual();
    updateRunningUsersIndividual();

    // Tampilan LCD untuk mode individual
    if (modeDaftarRFID) {
        // Tampilan sudah di handleRegistrationIndividual
    } else if (modeScanUlang) {
        int sisa = (batasWaktuScanUlang - (millis() - waktuMulaiScanUlang)) / 1000;
        lcd.setCursor(0, 1);
        lcd.print("Sisa: "); lcd.print(sisa); lcd.print(" detik    ");
    } else if (pesanScanAktif && millis() - waktuPesanScanAktif > 2000) {
        pesanScanAktif = false;
        lcd.clear(); lcd.print("Scan ulang RFID");
    } else if (pesanTungguUser && millis() - waktuPesanTungguUser > 2000) {
        pesanTungguUser = false;
        lcd.clear(); lcd.print("Scan ulang RFID");
    } else if (runningUsersIndividual.empty() && pendingUsersIndividual.empty()) {
        lcd.clear(); lcd.print("Sistem Ready");
    }
  }
  
  // Fungsi umum yang berjalan di kedua mode
  checkDeleteSchedules();
}

// =====================================================================================
// FUNGSI UMUM (digunakan di kedua mode atau independen)
// =====================================================================================
String getUID() {
  String uid = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    uid += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
    uid += String(mfrc522.uid.uidByte[i], HEX);
  }
  uid.toUpperCase();
  return uid;
}

String getISOTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("❌ Gagal ambil waktu lokal dari NTP");
    return "";
  }

  char buffer[30];
  strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
  return String(buffer);
}

void buzz(int durasi) {
  digitalWrite(BUZZER_PIN, HIGH);
  delay(durasi);
  digitalWrite(BUZZER_PIN, LOW);
}

void resetSystemState() {
  // Reset semua flag dan data untuk kedua mode
  countdownActive = false;
  eventStarted = false;
  waitingForGlobalStartButton = false;
  currentEventId = "";
  eventGlobalStartTime = 0;
  eventParticipants.clear(); // Clear data event

  modeDaftarRFID = false;
  modeScanUlang = false;
  pesanScanAktif = false;
  pesanTungguUser = false;
  runningUsersIndividual.clear(); // Clear data individual
  pendingUsersIndividual.clear(); // Clear data individual
  cachedUIDsIndividual.clear(); // Clear data individual
  
  deleteSchedules.clear();
  lcd.clear();
  lcd.print("Sistem Ready");
  Serial.println("Sistem telah direset.");
}

void checkDeleteSchedules() {
  unsigned long now = millis();
  for (auto it = deleteSchedules.begin(); it != deleteSchedules.end();) {
    if (now >= it->deleteTime) {
      String path = "/activities/" + it->uidTag; // Path ini berlaku untuk individual
      // Untuk event, kita tidak menghapus dari /activities, hanya dari eventParticipants map
      // dan status di /activities akan diubah oleh aplikasi admin
      if (Firebase.RTDB.deleteNode(&fbdo, path)) {
        Serial.println("Hapus data done: " + it->uidTag);
        // Hapus juga dari cachedUIDsIndividual
        cachedUIDsIndividual.erase(it->uidTag);
      } else {
        Serial.println("Gagal hapus data done: " + it->uidTag + " - " + fbdo.errorReason());
      }
      it = deleteSchedules.erase(it);
    } else {
      ++it;
    }
  }
}

// =====================================================================================
// FUNGSI MODE INDIVIDUAL (LOGIKA ASLI ANDA, DENGAN PENYESUAIAN NAMA)
// =====================================================================================
void handleRegistrationIndividual() {
  if (digitalRead(BUTTON_REG_PIN) == LOW && !modeDaftarRFID && pendingUsersIndividual.empty() && runningUsersIndividual.empty()) {
    delay(50);
    if (digitalRead(BUTTON_REG_PIN) == LOW) {
      while (digitalRead(BUTTON_REG_PIN) == LOW); // tunggu tombol dilepas
      modeDaftarRFID = true;
      waktuMulaiScan = millis(); // Gunakan waktuMulaiScan untuk timeout registrasi
      lcd.clear(); lcd.print("Scan Tag RFID");
    }
  }

  if (modeDaftarRFID) {
    if (millis() - waktuMulaiScan > 10000) {
      lcd.clear(); lcd.print("Scan Timeout!");
      delay(1000);
      lcd.clear(); lcd.print("Sistem Ready");
      modeDaftarRFID = false;
      return;
    }

    if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
      String scannedUID = getUID();
      buzz(100);
      lcd.clear(); lcd.print("Kirim ke RTDB");

      if (Firebase.RTDB.setString(&fbdo, "/gelang", scannedUID)) {
        lcd.clear(); lcd.print("Terkirim!");
        buzz(100);
        delay(2000);
        if (Firebase.ready()) {
          if (Firebase.RTDB.deleteNode(&fbdo, "/gelang")) {
            Serial.println("Node /gelang berhasil dihapus");
          } else {
            Serial.println("Gagal hapus /gelang: " + fbdo.errorReason());
          }
        }
      } else {
        Serial.println("Gagal kirim ke RTDB: " + fbdo.errorReason());
        lcd.clear(); lcd.print("Gagal kirim!");
        delay(2000);
      }
      lcd.clear(); lcd.print("Sistem Ready");
      modeDaftarRFID = false;
    }
  }
}

void cekTombolStartIndividual() {
  int reading = digitalRead(BUTTON_START_PIN);

  if (reading != stableStartState) {
    lastDebounceTime = millis();
    stableStartState = reading;
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (stableStartState == LOW && lastStartState == HIGH) {
      Serial.println("Tombol START Individual ditekan");

      if (modeScanUlang) {
        if (!pesanScanAktif) {
          lcd.clear(); lcd.print("Scan masih aktif");
          buzz(300);
          pesanScanAktif = true;
          waktuPesanScanAktif = millis();
        }
        lastStartState = stableStartState;
        return;
      }

      bool adaYangBelumSiap = false;
      unsigned long sekarang = millis();

      for (auto& p : pendingUsersIndividual) {
        if (!p.readyToStart && (sekarang - p.waktuMasuk < 60000)) {
          adaYangBelumSiap = true;
          break;
        }
      }

      if (adaYangBelumSiap) {
        if (!pesanTungguUser) {
          lcd.clear(); lcd.print("Tunggu user lain");
          buzz(300);
          pesanTungguUser = true;
          waktuPesanTungguUser = millis();
        }
        lastStartState = stableStartState;
        return;
      }

      for (auto it = pendingUsersIndividual.begin(); it != pendingUsersIndividual.end();) {
        if (!it->readyToStart && sekarang - it->waktuMasuk >= 60000) {
          Serial.println("Timeout user (tidak scan ulang): " + it->uidTag);
          it = pendingUsersIndividual.erase(it);
        } else {
          ++it;
        }
      }

      for (auto it = pendingUsersIndividual.begin(); it != pendingUsersIndividual.end();) {
        if (it->readyToStart) {
          Serial.println("Mulai aktivitas user: " + it->uidTag);
          Firebase.RTDB.setString(&fbdo, "/activities/" + it->uidTag + "/status", "running");
          RunningUserIndividual ru = {it->uidTag, it->uidApk, it->type, it->putaranTarget, 0, millis(), 0, false};
          runningUsersIndividual.push_back(ru);
          lcd.clear(); lcd.print("Aktivitas Dimulai");
          buzz(300);
          it = pendingUsersIndividual.erase(it);
        } else {
          ++it;
        }
      }
    }
    lastStartState = stableStartState;
  }
}

void cekFirebaseIndividual() {
  unsigned long currentMillis = millis();
  if (currentMillis - lastCheck < checkInterval) return;
  lastCheck = currentMillis;

  bool foundWaitingUser = false;

  if (!Firebase.RTDB.getString(&fbdo, "/activities")) {
    Serial.println("❌ Gagal ambil data string dari /activities: " + fbdo.errorReason());
    return;
  }

  String rawJson = fbdo.stringData();
  FirebaseJson rootJson;
  rootJson.setJsonData(rawJson);

  size_t len = rootJson.iteratorBegin();
  for (size_t i = 0; i < len; i++) {
    String uidTag, nestedRaw;
    int type;

    rootJson.iteratorGet(i, type, uidTag, nestedRaw);
    if (uidTag == "" || uidTag.length() < 6) continue;

    FirebaseJsonData jsonData;
    FirebaseJson nestedJson;
    nestedJson.setJsonData(nestedRaw);

    String activityStatus, activityType, uid_apk;
    int putaran = 0;

    if (nestedJson.get(jsonData, "status")) activityStatus = jsonData.stringValue;
    if (nestedJson.get(jsonData, "type")) activityType = jsonData.stringValue;
    if (nestedJson.get(jsonData, "uid_apk")) uid_apk = jsonData.stringValue;
    if (activityType == "lintasan" && nestedJson.get(jsonData, "putaran"))
      putaran = jsonData.intValue;

    if (activityStatus == "waiting_for_rfid") {
      foundWaitingUser = true;

      bool alreadyPending = false;
      for (auto& p : pendingUsersIndividual) {
        if (p.uidTag == uidTag) {
          alreadyPending = true;
          break;
        }
      }

      if (!alreadyPending) {
        PendingUserIndividual pu;
        pu.uidTag = uidTag;
        pu.uidApk = uid_apk;
        pu.type = activityType;
        pu.putaranTarget = putaran;
        pu.readyToStart = false;
        pu.waktuMasuk = millis();
        pendingUsersIndividual.push_back(pu);

        cachedUIDsIndividual[uidTag] = uid_apk;

        Serial.println("✅ Tambah pending user individual: " + uidTag + " / " + uid_apk);
        lcd.clear(); lcd.print("Scan ulang RFID");
      }
    }
  }
  rootJson.iteratorEnd();

  if (foundWaitingUser && !modeScanUlang) {
    modeScanUlang = true;
    waktuMulaiScanUlang = millis();
    Serial.println("⏱ Mode Scan Ulang Individual dimulai");
  }
}

void cekTimeoutScanUlangIndividual() {
  if (!modeScanUlang) return;

  if (millis() - waktuMulaiScanUlang > batasWaktuScanUlang) {
    Serial.println("⏳ Waktu scan ulang Individual habis");

    bool adaYangScan = false;
    for (auto& p : pendingUsersIndividual) {
      if (p.readyToStart) {
        adaYangScan = true;
        break;
      }
    }

    if (!adaYangScan) {
      bool masihAdaWaiting = false;
      if (Firebase.RTDB.getJSON(&fbdo, "/activities")) {
        FirebaseJson& result = fbdo.jsonObject();
        size_t len = result.iteratorBegin();
        for (size_t i = 0; i < len; i++) {
          String key, ignore;
          int type;
          result.iteratorGet(i, type, key, ignore);
          String status;
          FirebaseJsonData jsonData;
          result.get(jsonData, key + "/status");
          if (jsonData.stringValue == "waiting_for_rfid") {
            masihAdaWaiting = true;
            break;
          }
        }
        result.iteratorEnd();
      }

      if (masihAdaWaiting) {
        Serial.println("🔄 Tidak ada yang scan, reset timer Individual");
        waktuMulaiScanUlang = millis();
        return;
      } else {
        modeScanUlang = false;
        lcd.clear(); lcd.print("Menunggu START");
        return;
      }
    }

    for (auto it = pendingUsersIndividual.begin(); it != pendingUsersIndividual.end();) {
      if (!it->readyToStart) {
        Serial.println("❌ User timeout (tak scan): " + it->uidTag);
        it = pendingUsersIndividual.erase(it);
      } else {
        ++it;
      }
    }

    modeScanUlang = false;
    lcd.clear(); lcd.print("Tekan START");
    buzz(500);
  }
}

void handleRFIDScanIndividual() {
  if (millis() - lastRFIDScanTime < rfidScanInterval) return;
  lastRFIDScanTime = millis();

  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) return;

  String scannedUID = getUID();
  String apk;

  if (cachedUIDsIndividual.count(scannedUID) > 0) {
    apk = cachedUIDsIndividual[scannedUID];
  } else {
    if (Firebase.RTDB.getString(&fbdo, "/activities/" + scannedUID + "/uid_apk")) {
      apk = fbdo.stringData();
      cachedUIDsIndividual[scannedUID] = apk;
    } else {
      lcd.clear(); lcd.print("UID tidak valid");
      buzz(500);
      return;
    }
  }

    if (modeScanUlang) {
    for (auto& p : pendingUsersIndividual) {
      if (!p.readyToStart && p.uidTag == scannedUID) {
        p.readyToStart = true;
        Serial.println("✅ Scan ulang OK untuk: " + p.uidApk);
        buzz(100);
        lcd.clear(); lcd.print("Scan OK: "); lcd.print(p.uidApk.substring(0,6));
        delay(300);
        return;
      }
    }
    lcd.clear(); lcd.print("Bukan user aktif");
    buzz(200);
    return;
  }

  if (!modeScanUlang && runningUsersIndividual.empty()) return;

  for (auto& user : runningUsersIndividual) {
    if (!user.finished && user.uidApk == apk && millis() - user.lastScanTime > 500) {
      Serial.println("📍 USER match: " + user.uidApk);
      Serial.println("📍 SCAN LAP ke-" + String(user.scanCount + 1));
      user.scanCount++;
      user.lastScanTime = millis();
      buzz(100);
      lcd.setCursor(0, 1);
      lcd.print("Scan ke-"); lcd.print(user.scanCount); lcd.print("    ");

      String lapPath = "/activities/" + user.uidTag + "/laps/lap_" + String(user.scanCount) + "/timestamp";
      String waktuSekarang = getISOTime();
      bool ok = Firebase.RTDB.setString(&fbdo, lapPath.c_str(), waktuSekarang.c_str());

      if (ok) {
        Serial.println("✅ Timestamp berhasil disimpan: " + waktuSekarang);
      } else {
        Serial.println("❌ Gagal simpan timestamp: " + fbdo.errorReason());
      }

      bool selesai = false;
      if (user.type == "lintasan" && user.scanCount >= user.putaranTarget) selesai = true;
      if (user.type == "non-lintasan" && user.scanCount >= 1) selesai = true;

      if (selesai) {
        unsigned long durasi = millis() - user.startTime;
        Firebase.RTDB.setInt(&fbdo, "/activities/" + user.uidTag + "/duration", durasi);
        Firebase.RTDB.setString(&fbdo, "/activities/" + user.uidTag + "/status", "done");
        user.finished = true;
        lcd.clear(); lcd.print("Selesai!"); buzz(800);

        DeleteSchedule ds;
        ds.uidTag = user.uidTag;
        ds.deleteTime = millis() + 30000;
        deleteSchedules.push_back(ds);
      }
    }
  }
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

void updateRunningUsersIndividual() {
  bool adaAktif = false;
  for (auto& user : runningUsersIndividual) {
    if (!user.finished) {
      adaAktif = true;
      if (Firebase.RTDB.getString(&fbdo, "/activities/" + user.uidTag + "/status") && fbdo.stringData() == "done") {
        user.finished = true;
        continue;
      }

      unsigned long elapsed = millis() - user.startTime;
      int sec = (elapsed / 1000) % 60;
      int min = (elapsed / 60000) % 60;
      int hr = (elapsed / 3600000);

      lcd.setCursor(0, 0);
      lcd.print("Waktu: ");
      if (hr < 10) lcd.print("0");
      lcd.print(hr);
      lcd.print(":");
      if (min < 10) lcd.print("0");
      lcd.print(min);
      lcd.print(":");
      if (sec < 10) lcd.print("0");
      lcd.print(sec);
      lcd.print(" ");
    }
  }

  if (!adaAktif && !runningUsersIndividual.empty()) {
    lcd.clear();
    lcd.print("Semua selesai");
    buzz(800);
    delay(2000);
    lcd.clear();
    lcd.print("Sistem Ready");
  }

  runningUsersIndividual.erase(std::remove_if(runningUsersIndividual.begin(), runningUsersIndividual.end(), [](RunningUserIndividual & u) {
    return u.finished;
  }), runningUsersIndividual.end());
}

// =====================================================================================
// FUNGSI MODE EVENT (LOGIKA BARU)
// =====================================================================================
void listenToEventStatus() {
  if (Firebase.RTDB.getJSON(&fbdo, "/event_status")) {
    FirebaseJson &eventStatusJson = fbdo.jsonObject();

    size_t len = eventStatusJson.iteratorBegin();
    if (len > 0) {
      int type;
      String key, value;

      eventStatusJson.iteratorGet(0, type, key, value);
      eventStatusJson.iteratorEnd();

      String incomingEventId = key;

      if (currentEventId != incomingEventId) {
        currentEventId = incomingEventId;
        eventStarted = false;
        countdownActive = false;
        waitingForGlobalStartButton = false;
        eventParticipants.clear(); // Clear event participants for new event
        Serial.println("Current Event ID set to: " + currentEventId);
      }

      if (Firebase.RTDB.getJSON(&fbdo, "/event_status/" + currentEventId)) {
        FirebaseJson &eventData = fbdo.jsonObject();
        FirebaseJsonData jsonData;
        bool startedFromFirebase = false;
        bool scanModeFromFirebase = false;

        if (eventData.get(jsonData, "started")) startedFromFirebase = jsonData.boolValue;
        if (eventData.get(jsonData, "scan_mode")) scanModeFromFirebase = jsonData.boolValue;

        // Logika untuk memulai countdown (started: true, scan_mode: true)
        if (startedFromFirebase && scanModeFromFirebase && !countdownActive && !eventStarted && !waitingForGlobalStartButton) {
            countdownActive = true;
            waktuMulaiScan = millis();
            lcd.clear(); lcd.print("Scan: ");
            Serial.println("🔔 Countdown dimulai!");
        } 
        // Logika ketika countdown selesai (Firebase scan_mode: false, started: true)
        else if (startedFromFirebase && !scanModeFromFirebase && !eventStarted && !countdownActive) {
            waitingForGlobalStartButton = true;
            countdownActive = false;
            Serial.println("Countdown selesai, menunggu tombol START Event.");
        }
        // Logika untuk menghentikan event dari Firebase (started: false)
        else if (!startedFromFirebase && (eventStarted || countdownActive || waitingForGlobalStartButton)) {
          Serial.println("Event dihentikan dari Firebase, mereset sistem.");
          resetSystemState();
        }
      } else {
        Serial.println("Gagal membaca detail event untuk " + currentEventId);
      }
    } else {
      // Tidak ada event dalam /event_status, reset jika event sebelumnya aktif
      if (currentEventId != "") {
        Serial.println("Tidak ada event dalam /event_status, mereset sistem.");
        resetSystemState();
      }
    }
  }
}

void startScanCountdownLogic() {
  unsigned long batasWaktuScan = 20000;
  int sisaWaktu = (batasWaktuScan - (millis() - waktuMulaiScan)) / 1000;

  lcd.setCursor(0, 1);
  lcd.print("Sisa: ");
  if (sisaWaktu < 0) sisaWaktu = 0;
  lcd.print(sisaWaktu);
  lcd.print(" detik");

  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
    String scannedUID = getUID();
    buzz(100);
    lcd.setCursor(0, 0);
    lcd.print("UID: ");
    lcd.print(scannedUID);

    Firebase.RTDB.setBool(&fbdo, "/scan_result/" + currentEventId + "/" + scannedUID, true);
    Serial.println("UID dikirim ke RTDB: /scan_result/" + currentEventId + "/" + scannedUID + ": true");
    
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
  }

  if (millis() - waktuMulaiScan >= batasWaktuScan) {
    lcd.clear();
    lcd.print("Tekan START Event");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    countdownActive = false;
    waitingForGlobalStartButton = true;

    if (currentEventId != "") {
      Firebase.RTDB.setBool(&fbdo, "/event_status/" + currentEventId + "/scan_mode", false);
      Serial.println("Firebase /event_status/" + currentEventId + "/scan_mode diatur ke false.");
    }
    Serial.println("Countdown selesai, menunggu tombol START Event.");
  }
}

void handleStartButtonEvent() {
  int startButtonState = digitalRead(BUTTON_START_PIN);

  if (startButtonState != lastStartState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (startButtonState == LOW && stableStartState == HIGH) {
      Serial.println("handleStartButtonEvent() called.");

      if (waitingForGlobalStartButton && currentEventId != "") {
        eventStarted = true;
        waitingForGlobalStartButton = false;
        eventGlobalStartTime = millis();
        lcd.clear(); lcd.print("Event Dimulai!");
        buzz(500);
        Serial.println("✅ Event dimulai via tombol START.");

        // Reset hitungan peserta selesai dan total peserta setiap kali event dimulai
        totalParticipantsInEvent = 0;
        finishedParticipantsCount = 0; // Pastikan ini direset

        String scanResultPath = "/scan_result/" + currentEventId;
        String currentTime = getISOTime();

        if (Firebase.RTDB.getJSON(&fbdo, scanResultPath)) {
          String rawJson = fbdo.jsonString();
          Serial.println("Raw JSON for scan_result: " + rawJson);

          rawJson.replace("{", "");
          rawJson.replace("}", "");
          rawJson.trim();

          if (rawJson.length() == 0) {
            Serial.println("Tidak ada UID di /scan_result/" + currentEventId);
          } else {
            int startPos = 0;
            int endPos = 0;
            while (endPos != -1) {
              endPos = rawJson.indexOf(',', startPos);
              String pair;
              if (endPos == -1) {
                pair = rawJson.substring(startPos);
              } else {
                pair = rawJson.substring(startPos, endPos);
              }
              pair.trim();

              int colonPos = pair.indexOf(':');
              if (colonPos != -1) {
                String uidTag = pair.substring(0, colonPos);
                String value = pair.substring(colonPos + 1);

                uidTag.replace("\"", "");
                uidTag.trim();
                value.replace("\"", "");
                value.trim();
                value.toLowerCase();

                Serial.println("Manually parsed UID: " + uidTag + ", Value: " + value);

                if (uidTag.length() > 0 && value == "true") {
                  EventParticipant ep;
                  ep.uidTag = uidTag;
                  ep.lapCount = 0;
                  ep.lapStartTime = 0; // Penting: Ini harus 0 di awal, akan diset saat Lap 0 discan
                  ep.lastScanTime = 0;
                  ep.finished = false;

                  String activityPath = "/activities/" + uidTag;
                  if (Firebase.RTDB.getJSON(&fbdo, activityPath)) {
                    FirebaseJson &activityJson = fbdo.jsonObject();
                    FirebaseJsonData tempJsonData;
                    if (activityJson.get(tempJsonData, "putaran")) {
                      ep.putaranTarget = tempJsonData.intValue;
                      Serial.println("  Putaran target dari /activities: " + String(ep.putaranTarget));
                    } else {
                      ep.putaranTarget = 1;
                      Serial.println("  Putaran target tidak ditemukan, default ke 1.");
                    }
                    if (activityJson.get(tempJsonData, "type") && tempJsonData.stringValue == "non-lintasan") {
                      ep.putaranTarget = 1;
                      Serial.println("  Tipe non-lintasan, putaran target diatur ke 1.");
                    }
                  } else {
                    ep.putaranTarget = 1;
                    Serial.println("Warning: Gagal ambil putaranTarget untuk UID " + uidTag + ". Menggunakan default 1. Error: " + fbdo.errorReason());
                  }

                  eventParticipants[uidTag] = ep;
                  totalParticipantsInEvent++; // INI BARU: Tambahkan ke total peserta
                  Serial.println("  UID " + uidTag + " added to eventParticipants map. Current map size: " + String(eventParticipants.size()) + ". Total participants: " + String(totalParticipantsInEvent));

                } else {
                  Serial.println("Manually parsed UID " + uidTag + " ignored (value not 'true' or UID empty).");
                }
              }
              startPos = endPos + 1;
              if (endPos == -1) break;
            }
          }
          Serial.println("Jumlah eventParticipants setelah loop: " + String(eventParticipants.size()) + ". Final total participants: " + String(totalParticipantsInEvent));
        } else {
          Serial.println("❌ Gagal ambil data dari /scan_result/" + currentEventId + ": " + fbdo.errorReason());
        }
      }
    }
    stableStartState = startButtonState;
  }
  lastStartState = startButtonState;
}

void handleRFIDScanEvent() {
  if (countdownActive || modeDaftarRFID || !eventStarted) return;

  if (millis() - lastRFIDScanTime < rfidScanInterval) return;
  lastRFIDScanTime = millis();

  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) return;

  String scannedUID = getUID();
  Serial.println("RFID scanned in Event Mode: " + scannedUID);
  
  if (eventParticipants.count(scannedUID) > 0) {
    Serial.println("Scanned UID " + scannedUID + " found in eventParticipants.");
    EventParticipant& user = eventParticipants[scannedUID];

    if (!user.finished && millis() - user.lastScanTime > 500) {
      Serial.println("Processing scan for " + scannedUID + ". Current lapCount: " + String(user.lapCount));

      if (user.lapCount == 0 && user.lapStartTime == 0) { // Ini adalah scan pertama peserta (Lap 0)
          user.lapStartTime = millis();
          String currentTime = getISOTime();
          String lapPath = "/event_activity_data/" + currentEventId + "/" + user.uidTag + "/laps/lap_0/timestamp";
          
          if (Firebase.RTDB.setString(&fbdo, lapPath, currentTime)) {
            Serial.println("Lap 0 tercatat untuk " + user.uidTag + ": " + currentTime);
          } else {
            Serial.println("❌ Gagal mencatat Lap 0 untuk " + user.uidTag + ": " + fbdo.errorReason());
          }
          lcd.setCursor(0, 1);
          lcd.print("START: "); lcd.print(user.uidTag.substring(0,6));
          user.lastScanTime = millis();
          buzz(100);
          
          Serial.println("UID " + scannedUID + " (Lap 0) - Next expected lap is 1.");

      } else { // Ini adalah scan untuk Lap 1, Lap 2, dst.
          user.lapCount++;
          user.lastScanTime = millis();
          buzz(100);
          lcd.setCursor(0, 1);
          lcd.print("Lap ke-"); lcd.print(user.lapCount); lcd.print("    ");

          String lapPath = "/event_activity_data/" + currentEventId + "/" + user.uidTag + "/laps/lap_" + String(user.lapCount) + "/timestamp";
          String waktuSekarang = getISOTime();
          bool ok = Firebase.RTDB.setString(&fbdo, lapPath.c_str(), waktuSekarang.c_str());

          if (ok) {
            Serial.println("✅ Timestamp berhasil disimpan: " + waktuSekarang + " untuk Lap " + String(user.lapCount));
          } else {
            Serial.println("❌ Gagal simpan timestamp: " + fbdo.errorReason() + " untuk Lap " + String(user.lapCount));
          }

          bool selesai = false;
          if (user.putaranTarget > 0 && user.lapCount >= user.putaranTarget) {
              selesai = true;
              Serial.println("UID " + user.uidTag + " mencapai atau melebihi putaranTarget (" + String(user.putaranTarget) + "). Menandai selesai.");
          } else {
              Serial.println("UID " + user.uidTag + " belum mencapai putaranTarget (" + String(user.putaranTarget) + "). Lanjut.");
          }
          
          if (selesai) {
            unsigned long durasi = millis() - user.lapStartTime;
            Firebase.RTDB.setInt(&fbdo, "/event_activity_data/" + currentEventId + "/" + user.uidTag + "/duration", durasi);
            Serial.println("Durasi tercatat untuk " + user.uidTag + ": " + String(durasi) + " ms");
            
            Firebase.RTDB.setString(&fbdo, "/activities/" + user.uidTag + "/status", "done");
            user.finished = true;
            lcd.clear(); lcd.print("Selesai!"); buzz(800);

            finishedParticipantsCount++; // INI BARU: Inkrementasi hitungan peserta selesai
            Serial.println("Finished participants count: " + String(finishedParticipantsCount) + " / " + String(totalParticipantsInEvent));

            DeleteSchedule ds;
            ds.uidTag = user.uidTag;
            ds.deleteTime = millis() + 30000;
            deleteSchedules.push_back(ds);
            Serial.println("UID " + user.uidTag + " ditambahkan ke jadwal penghapusan.");
          }
      }
    } else if (user.finished) {
      lcd.setCursor(0, 1);
      lcd.print("Sudah Selesai!");
      buzz(200);
      Serial.println("Scanned UID " + scannedUID + " is already finished.");
    } else if (millis() - user.lastScanTime <= 500) {
      lcd.setCursor(0, 1);
      lcd.print("Scan terlalu cpt!");
      buzz(50);
      Serial.println("Scanned UID " + scannedUID + ": Scan terlalu cepat.");
    }
  } else {
    lcd.setCursor(0, 1);
    lcd.print("UID tidak valid!");
    Serial.println("UID tidak valid discan (Event Mode): " + scannedUID + ". Not found in eventParticipants.");
    buzz(300);
  }
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();

  // INI BARU: Panggil fungsi untuk memeriksa apakah event sudah selesai
  checkEventCompletion();
}

void checkEventCompletion() {
  if (eventStarted && totalParticipantsInEvent > 0 && finishedParticipantsCount >= totalParticipantsInEvent) {
    Serial.println("Semua peserta telah menyelesaikan event!");
    eventStarted = false; // Hentikan event
    currentEventId = ""; // Kosongkan ID event
    eventParticipants.clear(); // Hapus semua peserta dari map
    deleteSchedules.clear(); // Hapus jadwal penghapusan
    
    lcd.clear();
    lcd.print("Event Selesai!");
    lcd.setCursor(0, 1);
    lcd.print("System Ready.");
    buzz(1000); // Buzzer panjang untuk menandakan event selesai
    Serial.println("Event diakhiri. System Ready.");
  }
}

void updateEventParticipants() {
  // Hapus peserta yang sudah selesai dari map eventParticipants
  for (auto it = eventParticipants.begin(); it != eventParticipants.end();) {
    if (it->second.finished) {
      it = eventParticipants.erase(it);
    } else {
      ++it;
    }
  }
}
