#include <ArduinoJson.h>
#include <JsonLogic.h>

using json_logic::Json;
using json_logic::JsonLogic;

Json activeRule;

void performAction(const char* action)
{
  if (strcmp(action, "fan_on") == 0)
  {
    Serial.println("ACTION: turn the ventilation fan on");
    // Replace this with digitalWrite(), MQTT, a relay driver, and so on.
  }
  else if (strcmp(action, "send_alert") == 0)
  {
    Serial.println("ACTION: send an alert");
  }
  else
  {
    Serial.print("Unknown action: ");
    Serial.println(action);
  }
}

void setRule(JsonVariantConst definition)
{
  if (!definition["action"].is<const char*>() ||
      definition["when"].isNull())
  {
    Serial.println("A rule needs string 'action' and JsonLogic 'when' fields.");
    return;
  }

  activeRule.set(definition);
  Serial.print("Rule updated: ");
  serializeJson(activeRule, Serial);
  Serial.println();
}

void evaluate(JsonVariantConst data)
{
  if (activeRule.isNull())
  {
    Serial.println("No active rule.");
    return;
  }

  JsonLogic* engine = JsonLogic::GetInstance();
  Json result = engine->Apply(activeRule["when"], data);

  if (engine->HasError())
  {
    Serial.print("Rule error: ");
    Serial.println(engine->GetLastError().c_str());
    return;
  }

  Serial.print("Rule result: ");
  serializeJson(result, Serial);
  Serial.println();

  if (result.as<bool>())
  {
    performAction(activeRule["action"].as<const char*>());
  }
}

void handleMessage(const String& line)
{
  Json message;
  DeserializationError error = deserializeJson(message, line);
  if (error)
  {
    Serial.print("Invalid JSON: ");
    Serial.println(error.c_str());
    return;
  }

  if (!message["set_rule"].isNull())
  {
    setRule(message["set_rule"]);
  }
  else if (!message["evaluate"].isNull())
  {
    evaluate(message["evaluate"]);
  }
  else
  {
    Serial.println("Use a 'set_rule' or 'evaluate' message.");
  }
}

void setup()
{
  Serial.begin(115200);
  delay(1000);

  Serial.println("Dynamic JsonLogic rule example");
  Serial.println("Send one JSON object per line.");
  Serial.println(
      R"({"set_rule":{"action":"fan_on","when":{"and":[{">":[{"var":"temperature"},28]},{">":[{"var":"humidity"},65]}]}}})");
  Serial.println(
      R"({"evaluate":{"temperature":31.5,"humidity":72}})");

  Json initialRule;
  deserializeJson(
      initialRule,
      R"({"action":"fan_on","when":{"and":[{">":[{"var":"temperature"},28]},{">":[{"var":"humidity"},65]}]}})");
  setRule(initialRule);

  Json sampleData;
  deserializeJson(sampleData, R"({"temperature":31.5,"humidity":72})");
  evaluate(sampleData);
}

void loop()
{
  if (!Serial.available()) return;

  String line = Serial.readStringUntil('\n');
  line.trim();
  if (line.length() > 0) handleMessage(line);
}
