#include "sd_logger.h"
#include "config.h"
#include <SD.h>
#include <SPI.h>
#include <time.h>

static bool g_sd_ready = false;
static SPIClass *g_sd_spi = nullptr;

static const char *fault_text(FaultType fault) {
  switch (fault) {
    case FaultType::MeterRead:
      return "meter";
    case FaultType::Decode:
      return "decode";
    case FaultType::LoraTx:
      return "loratx";
    case FaultType::TimeSync:
      return "timesync";
    default:
      return "";
  }
}

static bool ensure_dir(const String &path) {
  if (SD.exists(path)) {
    return true;
  }
  return SD.mkdir(path);
}

static String format_date_utc(uint32_t ts_utc) {
  time_t t = static_cast<time_t>(ts_utc);
  struct tm tm_utc;
  gmtime_r(&t, &tm_utc);
  char buf[16];
  snprintf(buf, sizeof(buf), "%04d-%02d-%02d",
           tm_utc.tm_year + 1900,
           tm_utc.tm_mon + 1,
           tm_utc.tm_mday);
  return String(buf);
}

void sd_logger_init() {
  if (!ENABLE_SD_LOGGING) {
    g_sd_ready = false;
    return;
  }
  if (!g_sd_spi) {
    g_sd_spi = new SPIClass(HSPI);
  }
  g_sd_spi->begin(PIN_SD_SCK, PIN_SD_MISO, PIN_SD_MOSI, PIN_SD_CS);
  g_sd_ready = SD.begin(PIN_SD_CS, *g_sd_spi);
  if (SERIAL_DEBUG_MODE) {
    if (g_sd_ready) {
      uint8_t type = SD.cardType();
      uint64_t size = SD.cardSize();
      Serial.printf("sd: ok type=%u size=%llu\n", static_cast<unsigned>(type), static_cast<unsigned long long>(size));
    } else {
      Serial.println("sd: init failed");
    }
  }
}

bool sd_logger_is_ready() {
  return g_sd_ready;
}

void sd_logger_log_sample(const MeterData &data, bool include_error_text) {
  if (!g_sd_ready || data.ts_utc == 0) {
    return;
  }

  String root_dir = "/dd3";
  if (!ensure_dir(root_dir)) {
    return;
  }

  String sender_dir = root_dir + "/" + String(data.device_id);
  if (!ensure_dir(sender_dir)) {
    return;
  }

  String filename = sender_dir + "/" + format_date_utc(data.ts_utc) + ".csv";
  bool new_file = !SD.exists(filename);
  File f = SD.open(filename, FILE_APPEND);
  if (!f) {
    return;
  }

  if (new_file) {
    f.println("ts_utc,p_w,p1_w,p2_w,p3_w,e_kwh,bat_v,bat_pct,rssi,snr,err_m,err_d,err_tx,err_last");
  }

  f.print(data.ts_utc);
  f.print(',');
  f.print(data.total_power_w, 1);
  f.print(',');
  f.print(data.phase_power_w[0], 1);
  f.print(',');
  f.print(data.phase_power_w[1], 1);
  f.print(',');
  f.print(data.phase_power_w[2], 1);
  f.print(',');
  f.print(data.energy_total_kwh, 3);
  f.print(',');
  f.print(data.battery_voltage_v, 2);
  f.print(',');
  f.print(data.battery_percent);
  f.print(',');
  f.print(data.link_rssi_dbm);
  f.print(',');
  if (isnan(data.link_snr_db)) {
    f.print("");
  } else {
    f.print(data.link_snr_db, 1);
  }
  f.print(',');
  f.print(data.err_meter_read);
  f.print(',');
  f.print(data.err_decode);
  f.print(',');
  f.print(data.err_lora_tx);
  f.print(',');
  if (include_error_text && data.last_error != FaultType::None) {
    f.print(fault_text(data.last_error));
  }
  f.println();
  f.close();
}
