#include <WiFi.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Firebase_ESP_Client.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// ================= WIFI =================
#define WIFI_SSID "ivann"
#define WIFI_PASSWORD "12345678"

// ================= FIREBASE =================
#define DATABASE_URL "https://doorguard-7fe8c-default-rtdb.firebaseio.com/"
#define DATABASE_SECRET "wKeZFSXj8cyroV5xnjkMIsefRgSvklLI9dgnnQJW"

// ================= RFID =================
#define SS_PIN     5
#define RST_PIN    4
#define SCK_PIN    18
#define MISO_PIN   19
#define MOSI_PIN   23

// ================= RELAY =================
#define RELAY_PIN 26

// ================= DOOR SENSOR =================
#define DOOR_SENSOR_PIN 27

// ================= EXIT =================
#define EXIT_BUTTON_PIN 32 

// ================= LCD =================
#define SDA_LCD 21
#define SCL_LCD 22

// ================= OBJECT =================
MFRC522 rfid(SS_PIN, RST_PIN);
LiquidCrystal_I2C lcd(0x27, 16, 2);

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

// ================= VARIABLE =================
String uidString = "";
String userName = "";
String lastDoorCommand = "CLOSE";

bool autoCloseActive = false;
unsigned long doorTimer = 0;
unsigned long lastScanTime = 0;
unsigned long lastButtonTime = 0; 

bool lastDoorState = false;

// ================= SETUP =================
void setup()
{
  Serial.begin(115200);

  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH);

  pinMode(DOOR_SENSOR_PIN, INPUT_PULLUP);
  pinMode(EXIT_BUTTON_PIN, INPUT_PULLUP); 

  // LCD Initialization
  Wire.begin(SDA_LCD, SCL_LCD);
  lcd.init();
  lcd.backlight();

  lcd.setCursor(0,0);
  lcd.print("SMART DOOR LOCK");
  lcd.setCursor(0,1);
  lcd.print("STARTING...");
  delay(2000);

  // RFID Initialization
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN);
  rfid.PCD_Init();

  // WIFI Connection
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("\nWiFi Connected");

  // FIREBASE Configuration
  config.database_url = DATABASE_URL;
  config.signer.tokens.legacy_token = DATABASE_SECRET;

  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
  Serial.println("Firebase Connected");

  // Reset State Awal
  Firebase.RTDB.setString(&fbdo, "/rfid/doorControl", "CLOSE");
  Firebase.RTDB.setString(&fbdo, "/rfid/status", "IDLE");
  Firebase.RTDB.setString(&fbdo, "/rfid/lastScan", "IDLE");
}

// ================= LOOP =================
void loop()
{
  handleRemoteControl();
  handleRFID();
  handleExitButton(); 
  handleDoorSensor();
  handleAutoClose();
}

// ================= REMOTE CONTROL =================
void handleRemoteControl()
{
  if (Firebase.RTDB.getString(&fbdo, "/rfid/doorControl"))
  {
    String cmd = fbdo.stringData();

    if (cmd != lastDoorCommand)
    {
      lastDoorCommand = cmd;

      if (cmd == "OPEN")
      {
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("DOOR OPENED");
        lcd.setCursor(0,1);
        lcd.print("FROM APP");

        openDoor(false);
        Firebase.RTDB.setString(&fbdo, "/rfid/status", "DOOR OPENED FROM APP");
      }
      else if (cmd == "CLOSE")
      {
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("DOOR CLOSED");
        lcd.setCursor(0,1);
        lcd.print("FROM APP");

        closeDoor();
        Firebase.RTDB.setString(&fbdo, "/rfid/status", "DOOR CLOSED FROM APP");
      }
      delay(2000);
    }
  }
}

// ================= HANDLE RFID =================
void handleRFID()
{
  if (millis() - lastScanTime < 1000) return;

  if (!rfid.PICC_IsNewCardPresent()) return;
  if (!rfid.PICC_ReadCardSerial()) return;

  uidString = "";
  for (byte i = 0; i < rfid.uid.size; i++)
  {
    if (rfid.uid.uidByte[i] < 0x10) uidString += "0";
    uidString += String(rfid.uid.uidByte[i], HEX);
  }
  uidString.toLowerCase();

  Serial.print("UID Detected: ");
  Serial.println(uidString);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("SCANNING CARD...");
  lcd.setCursor(0,1);
  lcd.print(uidString);

  bool access = false;
  if (Firebase.RTDB.getBool(&fbdo, "/rfid/cards/" + uidString + "/active"))
  {
    if (fbdo.boolData()) access = true;
  }

  if (access)
  {
    if (Firebase.RTDB.getString(&fbdo, "/rfid/cards/" + uidString + "/name"))
    {
      userName = fbdo.stringData();
    } else {
      userName = "User";
    }
  }

  if (access)
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("ACCESS GRANTED");
    lcd.setCursor(0,1);
    

    String displayWelcome = "WELCOME " + userName;
    displayWelcome.toUpperCase(); 
    lcd.print(displayWelcome);

    Firebase.RTDB.setString(&fbdo, "/rfid/lastScan", uidString);
    Firebase.RTDB.setString(&fbdo, "/rfid/status", "ACCESS_GRANTED");
    Firebase.RTDB.setString(&fbdo, "/rfid/lastUser", userName);

    openDoor(true);
  }
  else
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("ACCESS DENIED");
    lcd.setCursor(0,1);
    lcd.print("UNKNOWN CARD");

    Firebase.RTDB.setString(&fbdo, "/rfid/lastScan", uidString);
    Firebase.RTDB.setString(&fbdo, "/rfid/status", "ACCESS_DENIED");
    Firebase.RTDB.setString(&fbdo, "/rfid/notification", "ACCESS_DENIED");
  }

  delay(3500);
  lastScanTime = millis();

  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();
}

// ================= HANDLE EXIT BUTTON =================
void handleExitButton()
{
  if (digitalRead(EXIT_BUTTON_PIN) == LOW && (millis() - lastButtonTime > 1000))
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("ACCESS GRANTED");
    lcd.setCursor(0,1);
    lcd.print("EXIT");

    Firebase.RTDB.setString(&fbdo, "/rfid/status", "OPENED FROM INSIDE");
    openDoor(true); 

    delay(1500);
    lastButtonTime = millis();
  }
}

// ================= OPEN DOOR =================
void openDoor(bool autoClose)
{
  digitalWrite(RELAY_PIN, LOW);
  Firebase.RTDB.setString(&fbdo, "/rfid/relay", "ON");

  delay(300);
  rfid.PCD_Init();

  if (autoClose)
  {
    autoCloseActive = true;
    doorTimer = millis();
  }
}

// ================= CLOSE DOOR =================
void closeDoor()
{
  digitalWrite(RELAY_PIN, HIGH);
  Firebase.RTDB.setString(&fbdo, "/rfid/relay", "OFF");

  autoCloseActive = false;
  delay(300);
  rfid.PCD_Init();
}

// ================= AUTO CLOSE =================
void handleAutoClose()
{
  if (autoCloseActive && (millis() - doorTimer >= 3000))
  {
    closeDoor();
  }
}

// ================= DOOR SENSOR =================
void handleDoorSensor()
{
  bool doorOpen = digitalRead(DOOR_SENSOR_PIN);

  if (doorOpen != lastDoorState)
  {
    lastDoorState = doorOpen;
    lcd.clear();

    if (doorOpen)
    {
      lcd.setCursor(0,0);
      lcd.print("STATUS:");
      lcd.setCursor(0,1);
      lcd.print("DOOR OPEN");

      Firebase.RTDB.setString(&fbdo, "/rfid/doorSensor", "OPEN");
    }
    else
    {
      lcd.setCursor(0,0);
      lcd.print("STATUS:");
      lcd.setCursor(0,1);
      lcd.print("DOOR CLOSED");

      Firebase.RTDB.setString(&fbdo, "/rfid/doorSensor", "CLOSED");
    }
  }
}