#include <Arduino.h>
#include <TBeamClock.h>
#include <TBeamLogger.h>
#include <TBeamStorage.h>
#include "tbeam_supreme_adapter.h"

#ifndef BOARD_ID
#define BOARD_ID "UNKNOWN"
#endif

#ifndef NODE_LABEL
#define NODE_LABEL "Unknown"
#endif

#ifndef CLOCK_TEST_ALLOW_SET
#define CLOCK_TEST_ALLOW_SET 0
#endif

namespace {

tbeam::TBeamClock clockService(Wire1);
tbeam::TBeamStorage storage(Serial);
tbeam::TBeamLogger Log;

XPowersLibInterface* pmu = nullptr;
String commandBuffer;
bool lastWasCr = false;
uint32_t sampleSeq = 0;
uint32_t lastSampleMs = 0;

void printDateTime(Print& out, const tbeam::DateTime& dt) {
  char iso[32];
  tbeam::TBeamClock::formatIsoUtc(dt, iso, sizeof(iso));
  out.print(iso);
}

void printHelp() {
  Serial.println("Commands:");
  Serial.println("  help");
  Serial.println("  show");
#if CLOCK_TEST_ALLOW_SET
  Serial.println("  set YYYY-MM-DD HH:MM:SS");
#else
  Serial.println("  set disabled; build with CLOCK_TEST_ALLOW_SET=1 to enable writes");
#endif
}

void showClock() {
  clockService.update();
  Serial.printf("rtc_ready=%s valid=%s low_voltage=%s error=%s\r\n",
                clockService.ready() ? "yes" : "no",
                clockService.valid() ? "yes" : "no",
                clockService.lowVoltage() ? "yes" : "no",
                clockService.lastError());
  Serial.print("rtc_utc=");
  printDateTime(Serial, clockService.lastRtc());
  Serial.printf(" epoch=%lld\r\n", (long long)clockService.lastEpoch());
}

void handleCommand(const String& raw) {
  String line = raw;
  line.trim();
  if (line.length() == 0) {
    return;
  }

  if (line == "help") {
    printHelp();
    return;
  }
  if (line == "show") {
    showClock();
    return;
  }
  if (line.startsWith("set ")) {
#if CLOCK_TEST_ALLOW_SET
    tbeam::DateTime dt{};
    if (!tbeam::TBeamClock::parseDateTime(line.c_str() + 4, dt)) {
      Serial.println("set parse failed");
      return;
    }
    if (!clockService.writeRtc(dt)) {
      Serial.println("RTC write failed");
      return;
    }
    Serial.println("RTC write succeeded");
    showClock();
#else
    Serial.println("RTC set disabled in this build");
#endif
    return;
  }

  Serial.printf("Unknown command: %s\r\n", line.c_str());
}

void pollSerialCommands() {
  while (Serial.available() > 0) {
    const char c = (char)Serial.read();
    if (c == '\r' || c == '\n') {
      if ((c == '\n' && lastWasCr) || (c == '\r' && !lastWasCr && commandBuffer.length() == 0)) {
        lastWasCr = (c == '\r');
        continue;
      }
      handleCommand(commandBuffer);
      commandBuffer = "";
      lastWasCr = (c == '\r');
    } else {
      lastWasCr = false;
      commandBuffer += c;
      if (commandBuffer.length() > 120) {
        commandBuffer = "";
        Serial.println("Input line too long; buffer cleared");
      }
    }
  }
}

void openClockLog() {
  if (!storage.ready()) {
    Serial.printf("Clock log skipped: storage error=%s\r\n", storage.lastError());
    return;
  }

  bool opened = false;
  if (clockService.valid()) {
    char runId[64];
    char path[112];
    tbeam::TBeamClock::makeRunId(clockService.lastRtc(), BOARD_ID, runId, sizeof(runId));
    snprintf(path, sizeof(path), "%s/%s.csv", storage.logDir(), runId);
    opened = Log.openLog(path);
  }
  if (!opened) {
    opened = Log.openUniqueLog(BOARD_ID, ".csv");
  }

  if (!opened) {
    Serial.printf("Clock log open failed: %s\r\n", storage.lastError());
    return;
  }

  Log.printf("# test: clock\r\n");
  Log.printf("# board_id: %s\r\n", BOARD_ID);
  Log.printf("# node_label: %s\r\n", NODE_LABEL);
  Log.printf("# log_path: %s\r\n", Log.currentLogPath());
  Log.printf("# rtc_valid_at_boot: %s\r\n", clockService.valid() ? "yes" : "no");
  Log.println("seq,millis,rtc_ready,rtc_valid,low_voltage,epoch,iso_utc,free_heap");
  Log.flush();
  Serial.printf("Clock log opened: %s\r\n", Log.currentLogPath());
}

}  // namespace

void setup() {
  Serial.begin(115200);
  delay(1500);
  Serial.println();
  Serial.printf("clock test boot board=%s label=%s\r\n", BOARD_ID, NODE_LABEL);

  if (!tbeam_supreme::initPmuForPeripherals(pmu, &Serial)) {
    Serial.println("PMU init failed; RTC read may fail");
  }

  tbeam::StorageConfig storageConfig;
  storageConfig.logDir = "/logs/clock";
  storageConfig.enablePinDumps = false;
  storage.begin(storageConfig);
  Log.begin(Serial, &storage);

  clockService.begin();
  showClock();
  openClockLog();
  printHelp();
}

void loop() {
  pollSerialCommands();
  storage.update();
  Log.update();

  const uint32_t now = millis();
  if ((uint32_t)(now - lastSampleMs) >= 1000) {
    lastSampleMs = now;
    clockService.update();

    char iso[32] = "";
    if (clockService.ready()) {
      tbeam::TBeamClock::formatIsoUtc(clockService.lastRtc(), iso, sizeof(iso));
    }

    Log.printf("%lu,%lu,%s,%s,%s,%lld,%s,%lu\r\n",
               (unsigned long)sampleSeq++,
               (unsigned long)now,
               clockService.ready() ? "yes" : "no",
               clockService.valid() ? "yes" : "no",
               clockService.lowVoltage() ? "yes" : "no",
               (long long)clockService.lastEpoch(),
               iso,
               (unsigned long)ESP.getFreeHeap());
  }
}
