#include "meter_manager.h"
#include "esp_log.h"

#include "meter_ade7758.h"
#include "meter_orno513.h"
#include "meter_orno516.h"
#include "meter_orno526.h"
#include "meter_dts6619.h"
#include "meter_dds661.h"
#include "meter_zigbee.h"
#include "meter_ea777.h"
#include "meter_dts024m.h"   

#include <string.h>

#include "network_events.h"
#include "meter_events.h"
#include "esp_event.h"
#include "esp_timer.h"

// NEW:
#include "storage_service.h"

static const char *TAG = "meter_manager";

// Tipos de medidores EVSE e GRID
static meter_type_t meter_evse_type = METER_TYPE_NONE;
static meter_type_t meter_grid_type = METER_TYPE_NONE;

#define STORE_NAMESPACE "meterconfig"
#define STORE_EVSE_MODEL "evse_model"
#define STORE_GRID_MODEL "grid_model"

// timeouts storage
#define STORAGE_TO pdMS_TO_TICKS(800)

// ------------------------------------------------------------------
// Helpers storage (robustos a queue cheia)
// ------------------------------------------------------------------

static esp_err_t storage_try_flush(void)
{
    return storage_flush_sync(pdMS_TO_TICKS(2000));
}

static esp_err_t storage_try_set_u8(const char *ns, const char *key, uint8_t v)
{
    for (int attempt = 0; attempt < 3; ++attempt)
    {
        esp_err_t err = storage_set_u8_async(ns, key, v);
        if (err == ESP_OK)
            return ESP_OK;

        if (err == ESP_ERR_TIMEOUT)
        {
            (void)storage_try_flush();
            continue;
        }
        return err;
    }
    return ESP_ERR_TIMEOUT;
}

static esp_err_t storage_get_u8_default(const char *ns, const char *key, uint8_t *out, uint8_t def)
{
    if (!out)
        return ESP_ERR_INVALID_ARG;

    uint8_t v = def;
    esp_err_t err = storage_get_u8_sync(ns, key, &v, STORAGE_TO);
    if (err == ESP_OK)
    {
        *out = v;
        return ESP_OK;
    }

    // se não existir / erro, devolve default
    *out = def;
    return err;
}

// ------------------------------------------------------------------
/*
static void meter_manager_network_event_handler(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
    (void)arg;
    (void)data;

    if (base != NETWORK_EVENTS)
        return;

    switch (event_id)
    {
    case NETWORK_EVENT_AP_STARTED:
        ESP_LOGD(TAG, "Recebido NETWORK_EVENT_AP_STARTED, parando medidor de grid");
        break;

    case NETWORK_EVENT_AP_STOP:
        ESP_LOGD(TAG, "Recebido NETWORK_EVENT_AP_STOP, reiniciando medidor de grid");
        break;

    case NETWORK_EVENT_STA_GOT_IP:
        ESP_LOGD(TAG, "Recebido NETWORK_EVENT_STA_GOT_IP");
        break;

    default:
        break;
    }
}
*/

// Função unificada para ler ou inicializar um modelo de medidor (via storage_service)
static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type)
{
    if (!type)
        return ESP_ERR_INVALID_ARG;

    // tenta ler
    uint8_t value = 0xFF;
    esp_err_t err = storage_get_u8_sync(STORE_NAMESPACE, key, &value, STORAGE_TO);

    if (err == ESP_OK && value < 255)
    {
        *type = (meter_type_t)value;
        ESP_LOGD(TAG, "Loaded meter type %u from storage key '%s/%s'", (unsigned)value, STORE_NAMESPACE, key);
        return ESP_OK;
    }

    // se não existir / inválido -> default NONE e grava
    *type = METER_TYPE_NONE;

    esp_err_t w = storage_try_set_u8(STORE_NAMESPACE, key, (uint8_t)(*type));
    if (w != ESP_OK)
        ESP_LOGE(TAG, "Failed to init key '%s/%s' to NONE: %s", STORE_NAMESPACE, key, esp_err_to_name(w));

    (void)storage_try_flush();

    ESP_LOGW(TAG, "Invalid/missing key '%s/%s' (read=%s), setting default (NONE)",
             STORE_NAMESPACE, key, esp_err_to_name(err));

    return ESP_OK; // seguimos com default
}

static esp_err_t write_meter_model_to_storage(const char *key, meter_type_t meter_type)
{
    esp_err_t err = storage_try_set_u8(STORE_NAMESPACE, key, (uint8_t)meter_type);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to write meter type to storage key '%s/%s': %s",
                 STORE_NAMESPACE, key, esp_err_to_name(err));
        return err;
    }

    (void)storage_try_flush();
    ESP_LOGD(TAG, "Saved meter type %d to storage key '%s/%s'", (int)meter_type, STORE_NAMESPACE, key);
    return ESP_OK;
}

/**
 * @brief Inicializa o sistema de meter manager.
 */
esp_err_t meter_manager_init(void)
{
    // garantir storage pronto
    esp_err_t s = storage_service_init();
    if (s != ESP_OK)
        ESP_LOGE(TAG, "storage_service_init failed: %s", esp_err_to_name(s));

    esp_err_t err;

    // Initialize GRID meter
    err = meter_manager_grid_init();
    if (err != ESP_OK)
        return err;

    // Regista handler para eventos de rede
    /*
    ESP_LOGD(TAG, "Registering network event handler");
    err = esp_event_handler_register(
        NETWORK_EVENTS,
        ESP_EVENT_ANY_ID,
        meter_manager_network_event_handler,
        NULL);
        */

    if (err != ESP_OK)
        return err;

    // Emite um evento inicial de configuração
    meter_config_event_t ev = {
        .grid_type = meter_manager_grid_get_model(),
        .evse_type = meter_manager_evse_get_model(),
        .timestamp_us = esp_timer_get_time()};
    esp_event_post(METER_EVENT,
                   METER_EVENT_CONFIG_UPDATED,
                   &ev,
                   sizeof(ev),
                   0);

    return ESP_OK;
}

esp_err_t meter_manager_start(void)
{
    // Start GRID meter
    return meter_manager_grid_start();
}

esp_err_t meter_manager_stop(void)
{
    // Stop GRID meter
    return meter_manager_grid_stop();
}

// ---------- EVSE ----------

esp_err_t meter_manager_evse_init()
{
    esp_err_t err = load_or_init_meter_model(STORE_EVSE_MODEL, &meter_evse_type);
    if (err != ESP_OK)
        return err;

    ESP_LOGD(TAG, "Initializing EVSE meter of type %s", meter_type_to_str(meter_evse_type));

    switch (meter_evse_type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        return meter_ade7758_init();
    case METER_TYPE_ORNO513:
        return meter_orno513_init();
    case METER_TYPE_ORNO516:
        return meter_orno516_init();
    case METER_TYPE_ORNO526:
        return meter_orno526_init();
    case METER_TYPE_DTS6619:
        return meter_dts6619_init();
    case METER_TYPE_DDS661:
        return meter_dds661_init();
    case METER_TYPE_EA777:
        return meter_ea777_init();
    case METER_TYPE_DTS024M:                 
        return meter_dts024m_init();         
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        return meter_zigbee_init();
    default:
        return ESP_ERR_INVALID_ARG;
    }
}

esp_err_t meter_manager_evse_start()
{
    meter_type_t type = meter_manager_evse_get_model();
    switch (type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        return meter_ade7758_start();
    case METER_TYPE_ORNO513:
        return meter_orno513_start();
    case METER_TYPE_ORNO516:
        return meter_orno516_start();
    case METER_TYPE_ORNO526:
        return meter_orno526_start();
    case METER_TYPE_DTS6619:
        return meter_dts6619_start();
    case METER_TYPE_DDS661:
        return meter_dds661_start();
    case METER_TYPE_EA777:
        return meter_ea777_start();
    case METER_TYPE_DTS024M:                 
        return meter_dts024m_start();        
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        return meter_zigbee_start();
    default:
        return ESP_ERR_INVALID_ARG;
    }
}

esp_err_t meter_manager_evse_stop(void)
{
    meter_type_t type = meter_manager_evse_get_model();
    switch (type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        meter_ade7758_stop();
        break;
    case METER_TYPE_ORNO513:
        meter_orno513_stop();
        break;
    case METER_TYPE_ORNO516:
        meter_orno516_stop();
        break;
    case METER_TYPE_ORNO526:
        meter_orno526_stop();
        break;
    case METER_TYPE_DTS6619:
        meter_dts6619_stop();
        break;
    case METER_TYPE_DDS661:
        meter_dds661_stop();
        break;
    case METER_TYPE_EA777:
        meter_ea777_stop();
        break;
    case METER_TYPE_DTS024M:                 
        meter_dts024m_stop();                
        break;
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        meter_zigbee_stop();
        break;
    default:
        return ESP_ERR_INVALID_ARG;
    }
    return ESP_OK;
}

// ---------- GRID ----------

esp_err_t meter_manager_grid_init()
{
    esp_err_t err = load_or_init_meter_model(STORE_GRID_MODEL, &meter_grid_type);
    if (err != ESP_OK)
        return err;

    ESP_LOGD(TAG, "Initializing GRID meter of type %s", meter_type_to_str(meter_grid_type));

    switch (meter_grid_type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        return meter_ade7758_init();
    case METER_TYPE_ORNO513:
        return meter_orno513_init();
    case METER_TYPE_ORNO516:
        return meter_orno516_init();
    case METER_TYPE_ORNO526:
        return meter_orno526_init();
    case METER_TYPE_DTS6619:
        return meter_dts6619_init();
    case METER_TYPE_DDS661:
        return meter_dds661_init();
    case METER_TYPE_EA777:
        return meter_ea777_init();
    case METER_TYPE_DTS024M:                 
        return meter_dts024m_init();         
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        return meter_zigbee_init();
    default:
        return ESP_ERR_INVALID_ARG;
    }
}

esp_err_t meter_manager_grid_start()
{
    meter_type_t type = meter_manager_grid_get_model();
    switch (type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        return meter_ade7758_start();
    case METER_TYPE_ORNO513:
        return meter_orno513_start();
    case METER_TYPE_ORNO516:
        return meter_orno516_start();
    case METER_TYPE_ORNO526:
        return meter_orno526_start();
    case METER_TYPE_DTS6619:
        return meter_dts6619_start();
    case METER_TYPE_DDS661:
        return meter_dds661_start();
    case METER_TYPE_EA777:
        return meter_ea777_start();
    case METER_TYPE_DTS024M:                 
        return meter_dts024m_start();        
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        return meter_zigbee_start();
    default:
        return ESP_ERR_INVALID_ARG;
    }
}

esp_err_t meter_manager_grid_stop(void)
{
    meter_type_t type = meter_manager_grid_get_model();
    switch (type)
    {
    case METER_TYPE_NONE:
        return ESP_OK;
    case METER_TYPE_ADE7758:
        meter_ade7758_stop();
        break;
    case METER_TYPE_ORNO513:
        meter_orno513_stop();
        break;
    case METER_TYPE_ORNO516:
        meter_orno516_stop();
        break;
    case METER_TYPE_ORNO526:
        meter_orno526_stop();
        break;
    case METER_TYPE_DTS6619:
        meter_dts6619_stop();
        break;
    case METER_TYPE_DDS661:
        meter_dds661_stop();
        break;
    case METER_TYPE_EA777:
        meter_ea777_stop();
        break;
    case METER_TYPE_DTS024M:                 
        meter_dts024m_stop();                
        break;
    case METER_TYPE_MONO_ZIGBEE:
    case METER_TYPE_TRIF_ZIGBEE:
        meter_zigbee_stop();
        break;
    default:
        return ESP_ERR_INVALID_ARG;
    }
    return ESP_OK;
}

// ---------- Utilidades / setters com evento ----------

esp_err_t meter_manager_evse_set_model(meter_type_t meter_type)
{
    esp_err_t err;

    err = meter_manager_evse_stop();
    if (err != ESP_OK)
        ESP_LOGW(TAG, "Failed to stop EVSE meter before changing model: %s", esp_err_to_name(err));

    meter_evse_type = meter_type;

    err = write_meter_model_to_storage(STORE_EVSE_MODEL, meter_evse_type);
    if (err != ESP_OK)
        return err;

    err = meter_manager_evse_init();
    if (err == ESP_OK)
        err = meter_manager_evse_start();

    meter_config_event_t ev = {
        .grid_type = meter_manager_grid_get_model(),
        .evse_type = meter_manager_evse_get_model(),
        .timestamp_us = esp_timer_get_time()};
    esp_err_t evt_err = esp_event_post(METER_EVENT, METER_EVENT_CONFIG_UPDATED, &ev, sizeof(ev), portMAX_DELAY);
    if (evt_err != ESP_OK)
        ESP_LOGW(TAG, "Failed to post METER_EVENT_CONFIG_UPDATED (EVSE): %s", esp_err_to_name(evt_err));

    return err;
}

esp_err_t meter_manager_grid_set_model(meter_type_t meter_type)
{
    esp_err_t err;

    err = meter_manager_grid_stop();
    if (err != ESP_OK)
        ESP_LOGW(TAG, "Failed to stop GRID meter before changing model: %s", esp_err_to_name(err));

    meter_grid_type = meter_type;

    err = write_meter_model_to_storage(STORE_GRID_MODEL, meter_grid_type);
    if (err != ESP_OK)
        return err;

    err = meter_manager_grid_init();
    if (err == ESP_OK)
        err = meter_manager_grid_start();

    meter_config_event_t ev = {
        .grid_type = meter_manager_grid_get_model(),
        .evse_type = meter_manager_evse_get_model(),
        .timestamp_us = esp_timer_get_time()};
    esp_err_t evt_err = esp_event_post(METER_EVENT, METER_EVENT_CONFIG_UPDATED, &ev, sizeof(ev), portMAX_DELAY);
    if (evt_err != ESP_OK)
        ESP_LOGW(TAG, "Failed to post METER_EVENT_CONFIG_UPDATED (GRID): %s", esp_err_to_name(evt_err));

    return err;
}

bool meter_manager_evse_is_enabled(void)
{
    return meter_manager_evse_get_model() != METER_TYPE_NONE;
}

meter_type_t meter_manager_evse_get_model(void) { return meter_evse_type; }
meter_type_t meter_manager_grid_get_model(void) { return meter_grid_type; }

const char *meter_type_to_str(meter_type_t type)
{
    switch (type)
    {
    case METER_TYPE_NONE:
        return "NENHUM";
    case METER_TYPE_ADE7758:
        return "IC ADE";
    case METER_TYPE_ORNO513:
        return "ORNO-513";
    case METER_TYPE_ORNO516:
        return "ORNO-516";
    case METER_TYPE_ORNO526:
        return "ORNO-526";
    case METER_TYPE_DTS6619:
        return "DTS-6619";
    case METER_TYPE_DDS661:
        return "DDS-661";
    case METER_TYPE_MONO_ZIGBEE:
        return "MONO-ZIGBEE";
    case METER_TYPE_TRIF_ZIGBEE:
        return "TRIF-ZIGBEE";
    case METER_TYPE_EA777:
        return "EA-777";
    case METER_TYPE_DTS024M:
        return "DTS-024M";
    default:
        return "NENHUM";
    }
}

meter_type_t string_to_meter_type(const char *str)
{
    if (!str)
        return METER_TYPE_NONE;

    if (strcmp(str, "IC ADE") == 0)
        return METER_TYPE_ADE7758;
    if (strcmp(str, "ORNO-513") == 0)
        return METER_TYPE_ORNO513;
    if (strcmp(str, "ORNO-516") == 0)
        return METER_TYPE_ORNO516;
    if (strcmp(str, "ORNO-526") == 0)
        return METER_TYPE_ORNO526;
    if (strcmp(str, "DTS-6619") == 0)
        return METER_TYPE_DTS6619;
    if (strcmp(str, "DDS-661") == 0)
        return METER_TYPE_DDS661;
    if (strcmp(str, "MONO-ZIGBEE") == 0)
        return METER_TYPE_MONO_ZIGBEE;
    if (strcmp(str, "TRIF-ZIGBEE") == 0)
        return METER_TYPE_TRIF_ZIGBEE;
    if (strcmp(str, "EA-777") == 0)
        return METER_TYPE_EA777;
    if (strcmp(str, "DTS-024M") == 0)   
        return METER_TYPE_DTS024M;

    return METER_TYPE_NONE;
}
