#include <WiFi.h>
#include <PubSubClient.h>
#include <Keypad.h>
#include <ESP32Servo.h>

// Pin definitions
#define PIR_PIN 14
#define TRIG_PIN 27
#define ECHO_PIN 26
#define LED_PIN 33
#define BUZZER_PIN 25
#define SERVO_PIN 13

// WiFi credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// MQTT Broker
const char* mqtt_server = "broker.hivemq.com";

WiFiClient espClient;
PubSubClient client(espClient);

Servo locker;

const int ROW_NUM = 4;
const int COL_NUM = 4;
char keys[ROW_NUM][COL_NUM] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROW_NUM] = {19, 18, 5, 17};
byte colPins[COL_NUM] = {16, 4, 2, 15};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROW_NUM, COL_NUM);

String correctPassword = "1234";
String inputPassword = "";

unsigned long presenceStart = 0;
bool presenceDetected = false;

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (int i = 0; i < length; i++) msg += (char)payload[i];

  if (String(topic) == "locker/control" && msg == "UNLOCK") {
    Serial.println(" Remote unlock requested via MQTT");

    // Unlock logic inline
    digitalWrite(LED_PIN, LOW);
    tone(BUZZER_PIN, 1000, 200);
    locker.write(90);
    delay(5000);
    locker.write(0);
    digitalWrite(LED_PIN, HIGH);
  }

  if (String(topic) == "locker/password") {
    Serial.print(" Password received via MQTT: ");
    Serial.println(msg);

    if (msg == correctPassword) {
      Serial.println(" Password correct via Node-RED!");

      // Unlock logic inline
      digitalWrite(LED_PIN, LOW);
      tone(BUZZER_PIN, 1000, 200);
      locker.write(90);
      delay(5000);
      locker.write(0);
      digitalWrite(LED_PIN, HIGH);
    } else {
      Serial.println(" Wrong password from Node-RED!");
      alert();
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("ESP32Client")) {
      Serial.println("connected");
      client.subscribe("locker/control");
      client.subscribe("locker/password");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 2 seconds");
      delay(2000);
    }
  }
}

long getDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  long duration = pulseIn(ECHO_PIN, HIGH, 30000);
  long distance = duration * 0.034 / 2;
  if (duration == 0) return 999;
  return distance;
}

void alert() {
  for (int i = 0; i < 3; i++) {
    digitalWrite(LED_PIN, HIGH);
    tone(BUZZER_PIN, 800);
    delay(300);
    digitalWrite(LED_PIN, LOW);
    noTone(BUZZER_PIN);
    delay(300);
  }
  digitalWrite(LED_PIN, HIGH);
}

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  digitalWrite(BUZZER_PIN, LOW);
  locker.attach(SERVO_PIN, 500, 2400);
  locker.write(0);

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  Serial.println("System ready, waiting for presence...");
}

void loop() {
  if (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    reconnect();
  }
  client.loop();

  long dist = getDistance();
  Serial.print("Distance: ");
  Serial.print(dist);
  Serial.println(" cm");
  client.publish("locker/distance", String(dist).c_str());

  if (dist < 100 && digitalRead(PIR_PIN) == HIGH) {
    Serial.println("Presence detected! You have 3 minutes.");
    presenceDetected = true;
    presenceStart = millis();
    inputPassword = "";

    while (millis() - presenceStart < 180000) {
      char key = keypad.getKey();
      if (key) {
        Serial.print("*");
        inputPassword += key;
        if (inputPassword.length() == correctPassword.length()) {
          if (inputPassword == correctPassword) {
            Serial.println("\nCorrect password!");

            // Unlock logic inline
            digitalWrite(LED_PIN, LOW);
            tone(BUZZER_PIN, 1000, 200);
            locker.write(90);
            delay(5000);
            locker.write(0);
            digitalWrite(LED_PIN, HIGH);
            break;
          } else {
            Serial.println("\nWrong password!");
            alert();
            break;
          }
        }
      }

      if (getDistance() > 120) {
        Serial.println("User left.");
        break;
      }
    }

    if (inputPassword.length() == 0 && (millis() - presenceStart) >= 180000) {
      Serial.println("Timeout! No input entered.");
      alert();
    }
  }

  delay(500);
}