#include "device_preferences_nvs.h"
#include "nvs.h"
#include <cstring>

namespace {
constexpr const char* keys[] = {"prefs_a", "prefs_b"};
struct Handle {
    nvs_handle_t value=0;
    ~Handle() { if(value) nvs_close(value); }
};
}
wearable::LoadedPreferences preferences_load() {
    Handle h;
    const auto opened=nvs_open("wearable", NVS_READONLY, &h.value);
    if(opened==ESP_ERR_NVS_NOT_FOUND) return {};
    if(opened!=ESP_OK) { wearable::LoadedPreferences p; p.read_only=true; return p; }
    std::array<uint8_t,wearable::MAX_BLOB_SIZE> data[2]{};
    size_t sizes[2]={data[0].size(),data[1].size()};
    bool read_error=false;
    for(int i=0;i<2;++i) {
        const auto result=nvs_get_blob(h.value,keys[i],data[i].data(),&sizes[i]);
        if(result!=ESP_OK) {
            sizes[i]=0;
            if(result!=ESP_ERR_NVS_NOT_FOUND) read_error=true;
        }
    }
    auto p=wearable::load(data[0].data(),sizes[0],data[1].data(),sizes[1]);
    p.read_only=p.read_only || read_error;
    return p;
}
esp_err_t preferences_save(wearable::LoadedPreferences& saved, const wearable::DevicePreferences& value) {
    if(saved.read_only || saved.generation==UINT32_MAX) return ESP_ERR_INVALID_STATE;
    wearable::Blob blob{};
    if(!wearable::encode(value,saved.generation+1,blob)) return ESP_ERR_INVALID_ARG;
    const int slot=saved.slot==0?1:0;
    {
        Handle h;
        auto result=nvs_open("wearable",NVS_READWRITE,&h.value);
        if(result!=ESP_OK) return result;
        result=nvs_set_blob(h.value,keys[slot],blob.data(),blob.size());
        if(result!=ESP_OK) return result;
        result=nvs_commit(h.value);
        if(result!=ESP_OK) return result; // Never erase or replace the previous slot.
    }
    Handle verify;
    auto result=nvs_open("wearable",NVS_READONLY,&verify.value);
    if(result!=ESP_OK) return result;
    wearable::Blob readback{}; size_t size=readback.size();
    result=nvs_get_blob(verify.value,keys[slot],readback.data(),&size);
    if(result!=ESP_OK) return result;
    if(size!=blob.size() || readback!=blob) return ESP_FAIL;
    saved.value=value; saved.generation++; saved.slot=slot; saved.migration_needed=false;
    return ESP_OK;
}
