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

// ================= NETWORK & CREDENTIAL CONFIGURATION =================
const char* WIFI_SSID     = "BERKA";   // YOUR WIFI SSID
const char* WIFI_PASSWORD = "BRK05011922";  // YOUR WIFI PASSWORD

// MQTT Broker Settings
const char* MQTT_BROKER   = "mqtt.solartools.com.tr";
const int   MQTT_PORT     = 1883;
const char* TOKEN         = "d-E180461453A8"; // Used as MQTT username

// MQTT Topics
const char* TELEMETRY_TOPIC  = "v1/devices/me/telemetry";
const char* ATTRIBUTES_TOPIC = "v1/devices/me/attributes";
const char* RPC_REQ_TOPIC    = "v1/devices/me/rpc/request/+";

// Client Instances
WiFiClient espClient;
PubSubClient client(espClient);

// Interval Settings
unsigned long lastTelemetryTime = 0;
const long TELEMETRY_INTERVAL  = 5000; // Publish telemetry every 5 seconds

// ================= RPC & SUBSCRIPTION CALLBACK =================
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("]: ");

  String message = "";
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // Incoming RPC requests or shared attribute updates can be handled here
}

// ================= WIFI CONNECTION =================
void setupWiFi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to WiFi network: ");
  Serial.println(WIFI_SSID);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi connected successfully!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
}

// ================= MQTT CONNECTION & RECONNECT =================
void reconnectMQTT() {
  while (!client.connected()) {
    Serial.print("Connecting to MQTT Broker (");
    Serial.print(MQTT_BROKER);
    Serial.print(")... ");

    // Generate a unique Client ID
    String clientId = "ESP32Client-" + String(random(0xffff), HEX);

    // ThingsBoard / SolarTools Authentication Method:
    // ClientID: Any string
    // Username: Device Access Token
    // Password: "" (Empty)
    if (client.connect(clientId.c_str(), TOKEN, "")) {
      Serial.println("CONNECTED!");

      // Subscribe to RPC request and attribute updates
      client.subscribe(RPC_REQ_TOPIC);
      client.subscribe(ATTRIBUTES_TOPIC);

      // Publish static metadata as device attributes upon connection
      StaticJsonDocument<128> attrDoc;
      attrDoc["firmware_version"] = "1.0.0";
      attrDoc["ip_address"]       = WiFi.localIP().toString();

      char attrBuffer[128];
      serializeJson(attrDoc, attrBuffer);
      client.publish(ATTRIBUTES_TOPIC, attrBuffer);

    } else {
      Serial.print("Failed! Connection state (rc): ");
      Serial.print(client.state());
      Serial.println(" -> Retrying in 5 seconds.");
      delay(5000);
    }
  }
}

// ================= PUBLISH SENSOR DATA =================
void sendTelemetryData() {
  // Simulated solar telemetry data (Replace with actual sensor readings)
  float panelVoltage   = random(220, 240) / 10.0; // 22.0V - 24.0V
  float panelCurrent   = random(10, 50) / 10.0;   // 1.0A - 5.0A
  float generatedPower = panelVoltage * panelCurrent;
  float temperature     = random(250, 400) / 10.0; // 25.0°C - 40.0°C

  // Construct JSON Payload
  StaticJsonDocument<256> doc;
  doc["voltage"]     = panelVoltage;
  doc["current"]     = panelCurrent;
  doc["power"]       = generatedPower;
  doc["temperature"] = temperature;

  char jsonBuffer[256];
  serializeJson(doc, jsonBuffer);

  // Publish payload to the telemetry topic
  if (client.publish(TELEMETRY_TOPIC, jsonBuffer)) {
    Serial.print("[Telemetry Published]: ");
    Serial.println(jsonBuffer);
  } else {
    Serial.println("[ERROR] Failed to publish telemetry.");
  }
}

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

  client.setServer(MQTT_BROKER, MQTT_PORT);
  client.setCallback(mqttCallback);
}

// ================= MAIN LOOP =================
void loop() {
  if (!client.connected()) {
    reconnectMQTT();
  }
  client.loop(); // Keeps the client active and processes incoming packets

  // Periodic telemetry publishing
  unsigned long now = millis();
  if (now - lastTelemetryTime >= TELEMETRY_INTERVAL) {
    lastTelemetryTime = now;
    sendTelemetryData();
  }
}