#include "SettingsStore.h"
#include "Config.h"
#include "Log.h"
#include <Preferences.h>

// testGroup/boardTest reuse the former zeroed reserved bytes, so version 11
// settings remain binary-compatible and keep the user's optical parameters.
namespace { constexpr uint16_t SETTINGS_VERSION = 11; constexpr char NAMESPACE[] = "opt-test"; }

void SettingsStore::defaults(Settings &s) const {
  // 2 kHz, 200 us .. 2 us, 5%, 1 s.
  s = {SETTINGS_VERSION, static_cast<uint8_t>(Role::SOLO),
    static_cast<uint8_t>(TestKind::OPTICAL), static_cast<uint8_t>(LightCode::HH),
    2, 6, 3, 2, 3, static_cast<uint8_t>(TestGroup::OPTICS),
    static_cast<uint8_t>(BoardTest::ADC), 0};
  s.checksum = settingsChecksum(s);
}

bool SettingsStore::valid(const Settings &s) const {
  return s.version == SETTINGS_VERSION && s.role <= static_cast<uint8_t>(Role::SLAVE) &&
    s.testGroup <= static_cast<uint8_t>(TestGroup::BOARD) &&
    s.boardTest <= static_cast<uint8_t>(BoardTest::RX_INPUT) &&
    s.testKind <= static_cast<uint8_t>(TestKind::DRIVER) &&
    s.lightCode <= static_cast<uint8_t>(LightCode::LL) &&
    (s.testKind != static_cast<uint8_t>(TestKind::DRIVER) ||
      s.role == static_cast<uint8_t>(Role::SOLO)) &&
    s.frequencyIndex < countOf(PWM_FREQUENCY_OPTIONS_HZ) &&
    s.maxPulseIndex < countOf(MAX_PULSE_OPTIONS_NS) &&
    s.minPulseIndex < countOf(MIN_PULSE_OPTIONS_NS) &&
    MAX_PULSE_OPTIONS_NS[s.maxPulseIndex] >= MIN_PULSE_OPTIONS_NS[s.minPulseIndex] &&
    static_cast<uint64_t>(MAX_PULSE_OPTIONS_NS[s.maxPulseIndex]) *
      PWM_FREQUENCY_OPTIONS_HZ[s.frequencyIndex] < 1000000000ULL &&
    s.accuracyIndex < countOf(ACCURACY_OPTIONS_PCT) &&
    s.timeIndex < countOf(TEST_TIME_OPTIONS_MS) &&
    s.checksum == settingsChecksum(s);
}

bool SettingsStore::load(Settings &s) {
  Preferences prefs;
  if (!prefs.begin(NAMESPACE, true)) { defaults(s); Log::event("NVS", "open for read FAILED; defaults selected"); return false; }
  const size_t got = prefs.getBytes("settings", &s, sizeof(s));
  prefs.end();
  if (got != sizeof(s) || !valid(s)) { defaults(s); Log::event("NVS", "missing/corrupt settings; defaults selected"); return false; }
  Log::event("NVS", "settings loaded and validated");
  return true;
}

bool SettingsStore::save(Settings &s) {
  s.version = SETTINGS_VERSION; s.checksum = settingsChecksum(s);
  if (!valid(s)) { Log::event("NVS", "save rejected: invalid settings"); return false; }
  Preferences prefs;
  if (!prefs.begin(NAMESPACE, false)) { Log::event("NVS", "open for write FAILED"); return false; }
  const bool ok = prefs.putBytes("settings", &s, sizeof(s)) == sizeof(s);
  prefs.end(); Log::printf("NVS", "settings save %s", ok ? "OK" : "FAILED"); return ok;
}

TestParams SettingsStore::params(const Settings &s) const {
  return {PWM_FREQUENCY_OPTIONS_HZ[s.frequencyIndex],
    MAX_PULSE_OPTIONS_NS[s.maxPulseIndex], MIN_PULSE_OPTIONS_NS[s.minPulseIndex],
    ACCURACY_OPTIONS_PCT[s.accuracyIndex], TEST_TIME_OPTIONS_MS[s.timeIndex]};
}
