#include <Arduino.h>
#include <TBeamClock.h>
#include <TBeamDisplay.h>
#include <TBeamLogger.h>
#include <TBeamStorage.h>
#include <TBeamWeb.h>

#ifndef BOARD_ID
#define BOARD_ID "UNKNOWN"
#endif

#ifndef NODE_LABEL
#define NODE_LABEL "Unknown"
#endif

#ifndef LOG_AP_IP_OCTET
#define LOG_AP_IP_OCTET 25
#endif

namespace {

tbeam::TBeamClock clockService(Wire1);
tbeam::TBeamDisplay display;
tbeam::TBeamStorage storage(Serial);
tbeam::TBeamLogger Log;
tbeam::TBeamWeb web(Serial);

uint32_t sampleSeq = 0;
uint32_t lastSampleMs = 0;
uint32_t lastDisplayMs = 0;

void openWebLog() {
  if (!storage.ready()) {
    Serial.printf("Web 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("Web log open failed: %s\r\n", storage.lastError());
    return;
  }

  Log.printf("# test: web_files\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.println("seq,millis,web_ready,stations,sd_ready,rtc_valid,free_heap");
  Log.flush();
  Serial.printf("Web log opened: %s\r\n", Log.currentLogPath());
}

void drawStatus() {
  char ipLine[32];
  char webLine[32];
  char sdLine[32];
  char logLine[32];

  snprintf(ipLine, sizeof(ipLine), "%s", web.ip().toString().c_str());
  snprintf(webLine, sizeof(webLine), "AP: %s", web.ssid());
  snprintf(sdLine, sizeof(sdLine), "SD:%s STA:%u", storage.ready() ? "ok" : "bad", (unsigned)web.stationCount());
  snprintf(logLine, sizeof(logLine), "Log:%s", Log.storageReady() ? "open" : "serial");

  display.showLines("Web Files", webLine, ipLine, sdLine, logLine, BOARD_ID);
}

}  // namespace

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

  display.begin();
  display.showBoot("Web Files", BOARD_ID, "starting services");

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

  clockService.begin();
  openWebLog();

  tbeam::WebConfig webConfig;
  webConfig.ssidPrefix = "GPSQA";
  webConfig.boardId = BOARD_ID;
  webConfig.ipOctet = LOG_AP_IP_OCTET;
  webConfig.enableDelete = true;
  web.begin(storage, webConfig);

  Serial.printf("Web UI: http://%s/\r\n", web.ip().toString().c_str());
  Serial.printf("SSID: %s\r\n", web.ssid());
  drawStatus();
}

void loop() {
  storage.update();
  clockService.update();
  web.update();
  Log.update();

  const uint32_t now = millis();
  if ((uint32_t)(now - lastSampleMs) >= 1000) {
    lastSampleMs = now;
    Log.printf("%lu,%lu,%s,%u,%s,%s,%lu\r\n",
               (unsigned long)sampleSeq++,
               (unsigned long)now,
               web.ready() ? "yes" : "no",
               (unsigned)web.stationCount(),
               storage.ready() ? "yes" : "no",
               clockService.valid() ? "yes" : "no",
               (unsigned long)ESP.getFreeHeap());
  }

  if ((uint32_t)(now - lastDisplayMs) >= 2500) {
    lastDisplayMs = now;
    drawStatus();
  }
}
