#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLEScan.h"
#include "BLEAdvertisedDevice.h"
#include "BLEClient.h"

// =======================
// 🔧 Wi-Fi Configuration
// =======================
const char* ssid = "WIFI";
const char* password = "PASS";

// ===========================
// 🔥 Firebase (Firestore) Configuration
// ===========================
const char* PROJECT_ID = "project-id";
const char* API_KEY = "api-key";

// ======================
// 🔗 BLE Configuration
// ======================
#define SERVICE_UUID        "0000xxxx-0000-1000-8000-00805f9b34fb"  // Generic BLE service UUID
#define CHARACTERISTIC_UUID "0000yyyy-0000-1000-8000-00805f9b34fb"  // Characteristic that notifies barcode

BLEClient* pClient;
BLERemoteService* pRemoteService;
BLERemoteCharacteristic* pRemoteCharacteristic;
BLEScan* pBLEScan;

bool deviceConnected = false;
bool doConnect = false;
bool doScan = false;
BLEAdvertisedDevice* myDevice;

// ==========================
// 🔁 BLE Notification Callback
// ==========================
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                           uint8_t* pData, size_t length, bool isNotify) {
  Serial.print("📡 Barcode received: ");
  String barcode = "";
  for (int i = 0; i < length; i++) {
    barcode += (char)pData[i];
    Serial.print((char)pData[i]);
  }
  Serial.println();

  // 🔍 Process barcode: query Firestore and update quantity
  String documentId = findDocumentByCode(barcode);
  if (documentId == "") {
    Serial.println("❌ Document not found.");
    return;
  }

  Serial.println("✅ Document ID: " + documentId);

  int currentQty = getCurrentQuantity(documentId);
  Serial.println("📦 Current quantity: " + String(currentQty));

  if (currentQty > 0) {
    updateQuantity(documentId, currentQty - 1);
    Serial.println("🔻 New quantity: " + String(currentQty - 1));
  } else {
    Serial.println("⚠️ No quantity available.");
  }
}

// ==========================
// 🔁 BLE Scan Callback
// ==========================
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("🔍 Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    // Match device name (replace with actual scanner name)
    if (advertisedDevice.getName() == "BLE_SCANNER") {
      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;
      Serial.println("✅ Scanner found!");
    }
  }
};

// ==========================
// 🔌 Connect to BLE Scanner
// ==========================
bool connectToServer() {
  Serial.print("🔗 Connecting to: ");
  Serial.println(myDevice->getAddress().toString().c_str());

  pClient = BLEDevice::createClient();
  pClient->connect(myDevice);
  Serial.println("✅ Connected to BLE device");

  // Try known services and characteristics
  const char* serviceUUIDs[] = {
    "1812",  // HID
    "180f",  // Battery
    "180a",  // Info
    "1800",  // Generic Access
    "1801"   // GATT
  };

  const char* charUUIDs[] = {
    "2a4d",  // HID Report
    "2a00",  // Device Name
    "0000ae04-0000-1000-8000-00805f9b34fb"  // Custom Notify
  };

  for (int i = 0; i < 5; i++) {
    pRemoteService = pClient->getService(serviceUUIDs[i]);
    if (pRemoteService != nullptr) {
      Serial.println("🔎 Service found: " + String(serviceUUIDs[i]));
      for (int j = 0; j < 3; j++) {
        pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUIDs[j]);
        if (pRemoteCharacteristic != nullptr && pRemoteCharacteristic->canNotify()) {
          pRemoteCharacteristic->registerForNotify(notifyCallback);
          Serial.println("✅ Notifications registered.");
          return true;
        }
      }
    }
  }

  Serial.println("❌ No usable BLE services found.");
  pClient->disconnect();
  return false;
}

// ============================
// 🔍 Find Document by Code
// ============================
String findDocumentByCode(String code) {
  String url = "https://firestore.googleapis.com/v1/projects/";
  url += PROJECT_ID;
  url += "/databases/(default)/documents:runQuery?key=" + String(API_KEY);

  String jsonQuery = "{"
    "\"structuredQuery\": {"
      "\"from\": [{\"collectionId\": \"prodotti\"}],"
      "\"where\": {"
        "\"fieldFilter\": {"
          "\"field\": {\"fieldPath\": \"codice\"},"
          "\"op\": \"EQUAL\","
          "\"value\": {\"stringValue\": \"" + code + "\"}"
        "}"
      "},"
      "\"limit\": 1"
    "}"
  "}";

  HTTPClient http;
  http.begin(url);
  http.addHeader("Content-Type", "application/json");
  int httpCode = http.POST(jsonQuery);

  if (httpCode == 200) {
    String response = http.getString();
    int nameIndex = response.indexOf("\"name\":");
    if (nameIndex > 0) {
      int start = response.indexOf("\"", nameIndex + 7) + 1;
      int end = response.indexOf("\"", start);
      String fullPath = response.substring(start, end);
      int lastSlash = fullPath.lastIndexOf("/");
      return fullPath.substring(lastSlash + 1);
    }
  }

  http.end();
  return "";
}

// ============================
// 🔢 Get Current Quantity
// ============================
int getCurrentQuantity(String documentId) {
  String url = "https://firestore.googleapis.com/v1/projects/" + String(PROJECT_ID) +
               "/databases/(default)/documents/prodotti/" + documentId + "?key=" + API_KEY;

  HTTPClient http;
  http.begin(url);
  int httpCode = http.GET();

  if (httpCode == 200) {
    String response = http.getString();
    int idx = response.indexOf("\"integerValue\"");
    if (idx > 0) {
      int start = response.indexOf("\"", idx + 15) + 1;
      int end = response.indexOf("\"", start);
      return response.substring(start, end).toInt();
    }
  }

  http.end();
  return -1;
}

// ============================
// 🛠️ Update Quantity
// ============================
void updateQuantity(String documentId, int newQuantity) {
  String url = "https://firestore.googleapis.com/v1/projects/" + String(PROJECT_ID) +
               "/databases/(default)/documents/prodotti/" + documentId +
               "?key=" + API_KEY + "&updateMask.fieldPaths=quantita";

  String payload = "{\"fields\":{\"quantita\":{\"integerValue\":\"" + String(newQuantity) + "\"}}}";

  HTTPClient http;
  http.begin(url);
  http.addHeader("Content-Type", "application/json");
  int httpCode = http.PATCH(payload);

  if (httpCode == 200) {
    Serial.println("✅ Quantity updated successfully.");
  } else {
    Serial.print("❌ Update failed: ");
    Serial.println(httpCode);
  }

  http.end();
}

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

  // Wi-Fi connection
  WiFi.begin(ssid, password);
  Serial.print("🔌 Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\n✅ WiFi connected!");

  // BLE setup
  BLEDevice::init("ESP32_BLE_Client");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
}

// ============================
// 🔁 Loop
// ============================
void loop() {
  if (doConnect) {
    if (connectToServer()) {
      Serial.println("✅ Connected to BLE scanner.");
    } else {
      Serial.println("❌ BLE connection failed.");
    }
    doConnect = false;
  }

  if (deviceConnected) {
    // Passive wait; data will be handled via notifyCallback
    delay(1000);
  } else if (doScan) {
    BLEDevice::getScan()->start(0);  // Continue scanning if disconnected
  }

  delay(1000);
}

