#pragma once

#include <stdint.h>
#include <stddef.h>

#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "nvs.h" // para ESP_ERR_NVS_INVALID_LENGTH

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * NVS limita namespace e key a 15 chars (+ '\0')
 */
#define STORAGE_NS_MAX_LEN 16
#define STORAGE_KEY_MAX_LEN 16

#ifndef STORAGE_QUEUE_LEN
#define STORAGE_QUEUE_LEN 32
#endif

#ifndef STORAGE_MAX_PENDING
#define STORAGE_MAX_PENDING 48
#endif

#ifndef STORAGE_COMMIT_DEBOUNCE_MS
#define STORAGE_COMMIT_DEBOUNCE_MS 500
#endif

#ifndef STORAGE_MAX_VALUE_BYTES
#define STORAGE_MAX_VALUE_BYTES 96
#endif

    /**
     * Inicializa o serviço e cria a task interna.
     *
     * Requisitos:
     * - nvs_flash_init() deve ter sido chamado antes.
     */
    esp_err_t storage_service_init(void);

    // -------------------- Async setters (não bloqueiam; commit é debounced) --------------------

    esp_err_t storage_set_u8_async(const char *ns, const char *key, uint8_t v);
    esp_err_t storage_set_u16_async(const char *ns, const char *key, uint16_t v);
    esp_err_t storage_set_u32_async(const char *ns, const char *key, uint32_t v);

    esp_err_t storage_set_str_async(const char *ns, const char *key, const char *str);
    esp_err_t storage_set_blob_async(const char *ns, const char *key, const void *data, size_t len);

    esp_err_t storage_erase_key_async(const char *ns, const char *key);

    /** Força commit imediato (async). */
    esp_err_t storage_flush_async(void);

    // -------------------- Sync getters (bloqueiam até ler do NVS/pending) --------------------

    esp_err_t storage_get_u8_sync(const char *ns, const char *key, uint8_t *out, TickType_t to);
    esp_err_t storage_get_u16_sync(const char *ns, const char *key, uint16_t *out, TickType_t to);
    esp_err_t storage_get_u32_sync(const char *ns, const char *key, uint32_t *out, TickType_t to);

    esp_err_t storage_get_str_sync(const char *ns, const char *key, char *out, size_t out_sz, TickType_t to);

    /**
     * Blob sync (semântica semelhante a nvs_get_blob):
     * - Se out == NULL: devolve ESP_OK e coloca em *inout_len o tamanho requerido
     * - Se out != NULL e *inout_len < requerido: devolve ESP_ERR_NVS_INVALID_LENGTH e atualiza *inout_len com requerido
     * - Se OK: copia e atualiza *inout_len com o tamanho real
     */
    esp_err_t storage_get_blob_sync(const char *ns, const char *key, void *out, size_t *inout_len, TickType_t to);

    /** Força commit imediato (sync). */
    esp_err_t storage_flush_sync(TickType_t to);

    // -------------------- (upgrade opcional) Sync setters --------------------
    // Úteis no boot/migração quando queres garantia forte e/ou a fila async pode estar cheia.

    esp_err_t storage_set_u8_sync(const char *ns, const char *key, uint8_t v, TickType_t to);
    esp_err_t storage_set_u16_sync(const char *ns, const char *key, uint16_t v, TickType_t to);
    esp_err_t storage_set_u32_sync(const char *ns, const char *key, uint32_t v, TickType_t to);

    esp_err_t storage_set_str_sync(const char *ns, const char *key, const char *str, TickType_t to);
    esp_err_t storage_set_blob_sync(const char *ns, const char *key, const void *data, size_t len, TickType_t to);

    esp_err_t storage_erase_key_sync(const char *ns, const char *key, TickType_t to);

#ifdef __cplusplus
}
#endif
