#include "json_codec.h"
#include <ArduinoJson.h>
#include <limits.h>
#include <math.h>

static constexpr size_t STATE_JSON_DOC_CAPACITY = 512;

static float round2(float value) {
  if (isnan(value)) {
    return value;
  }
  return roundf(value * 100.0f) / 100.0f;
}

static int32_t round_to_i32(float value) {
  if (isnan(value)) {
    return 0;
  }
  long rounded = lroundf(value);
  if (rounded > INT32_MAX) {
    return INT32_MAX;
  }
  if (rounded < INT32_MIN) {
    return INT32_MIN;
  }
  return static_cast<int32_t>(rounded);
}

static const char *short_id_from_device_id(const char *device_id) {
  if (!device_id) {
    return "";
  }
  size_t len = strlen(device_id);
  if (len >= 4) {
    return device_id + (len - 4);
  }
  return device_id;
}

static void format_float_2(char *buf, size_t buf_len, float value) {
  if (!buf || buf_len == 0) {
    return;
  }
  if (isnan(value)) {
    snprintf(buf, buf_len, "null");
    return;
  }
  snprintf(buf, buf_len, "%.2f", round2(value));
}

static void set_int_or_null(JsonDocument &doc, const char *key, float value) {
  if (!key || key[0] == '\0') {
    return;
  }
  if (isnan(value)) {
    doc[key] = nullptr;
    return;
  }
  doc[key] = round_to_i32(value);
}

bool meterDataToJson(const MeterData &data, String &out_json) {
  StaticJsonDocument<STATE_JSON_DOC_CAPACITY> doc;
  doc["id"] = short_id_from_device_id(data.device_id);
  doc["ts"] = data.ts_utc;
  char buf[16];
  format_float_2(buf, sizeof(buf), data.energy_total_kwh);
  doc["e_kwh"] = serialized(buf);
  set_int_or_null(doc, "p_w", data.total_power_w);
  set_int_or_null(doc, "p1_w", data.phase_power_w[0]);
  set_int_or_null(doc, "p2_w", data.phase_power_w[1]);
  set_int_or_null(doc, "p3_w", data.phase_power_w[2]);
  format_float_2(buf, sizeof(buf), data.battery_voltage_v);
  doc["bat_v"] = serialized(buf);
  doc["bat_pct"] = data.battery_percent;
  if (data.link_valid) {
    doc["rssi"] = data.link_rssi_dbm;
    doc["snr"] = data.link_snr_db;
  }
  if (data.err_meter_read > 0) {
    doc["err_m"] = data.err_meter_read;
  }
  if (data.err_decode > 0) {
    doc["err_d"] = data.err_decode;
  }
  if (data.err_lora_tx > 0) {
    doc["err_tx"] = data.err_lora_tx;
  }
  doc["err_last"] = static_cast<uint8_t>(data.last_error);
  doc["rx_reject"] = data.rx_reject_reason;
  doc["rx_reject_text"] = rx_reject_reason_text(static_cast<RxRejectReason>(data.rx_reject_reason));

  out_json = "";
  size_t len = serializeJson(doc, out_json);
  return len > 0;
}
