#include "meters_settings_api.h"
#include "meter_manager.h" // Atualizado para usar o novo manager
#include "meter_events.h"
#include "esp_event.h"
#include "esp_log.h"
#include "cJSON.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include <string.h>

static const char *TAG = "meters_settings_api";

typedef struct
{
    meter_event_data_t last_grid;
    meter_event_data_t last_evse;
    bool has_grid;
    bool has_evse;
    int64_t ts_grid_us;
    int64_t ts_evse_us;
    SemaphoreHandle_t mtx;
} meters_cache_t;

static meters_cache_t g_cache = {0};

static void cache_init_once(void)
{
    if (!g_cache.mtx)
    {
        g_cache.mtx = xSemaphoreCreateMutex();
    }
}

static void copy_event_locked(const meter_event_data_t *src)
{
    if (!src)
        return;
    if (strcmp(src->source ? src->source : "", "EVSE") == 0)
    {
        g_cache.last_evse = *src;
        g_cache.has_evse = true;
        g_cache.ts_evse_us = esp_timer_get_time();
    }
    else
    {
        // Default trate como GRID
        g_cache.last_grid = *src;
        g_cache.has_grid = true;
        g_cache.ts_grid_us = esp_timer_get_time();
    }
}

// ---------- Event handler (atualiza cache) ----------
static void meters_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
    if (base != METER_EVENT || id != METER_EVENT_DATA_READY || data == NULL)
        return;

    cache_init_once();
    if (xSemaphoreTake(g_cache.mtx, pdMS_TO_TICKS(10)) == pdTRUE)
    {
        copy_event_locked((const meter_event_data_t *)data);
        xSemaphoreGive(g_cache.mtx);
    }
}

// ---------- Helpers JSON ----------
static cJSON *meter_event_to_json(const meter_event_data_t *m, int64_t ts_us)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "source", m->source ? m->source : "GRID");
    cJSON_AddNumberToObject(root, "frequency", m->frequency);
    cJSON_AddNumberToObject(root, "powerFactor", m->power_factor);
    cJSON_AddNumberToObject(root, "totalEnergy", m->total_energy);

    // timestamp relativo (segundos desde boot) e em micros
    cJSON_AddNumberToObject(root, "timestampUs", (double)ts_us);

    // arrays
    cJSON *vr = cJSON_CreateArray();
    for (int i = 0; i < 3; i++)
        cJSON_AddItemToArray(vr, cJSON_CreateNumber(m->vrms[i]));
    cJSON *ir = cJSON_CreateArray();
    for (int i = 0; i < 3; i++)
        cJSON_AddItemToArray(ir, cJSON_CreateNumber(m->irms[i]));
    cJSON *pw = cJSON_CreateArray();
    for (int i = 0; i < 3; i++)
        cJSON_AddItemToArray(pw, cJSON_CreateNumber(m->watt[i]));
    cJSON_AddItemToObject(root, "vrms", vr);
    cJSON_AddItemToObject(root, "irms", ir);
    cJSON_AddItemToObject(root, "watt", pw);
    return root;
}

// ---------- HTTP GET /api/v1/meters/live ----------
static esp_err_t meters_live_get_handler(httpd_req_t *req)
{
    cache_init_once();

    char query[64] = {0};
    char source[16] = {0};
    bool want_grid = true, want_evse = true;

    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK)
    {
        if (httpd_query_key_value(query, "source", source, sizeof(source)) == ESP_OK)
        {
            if (strcasecmp(source, "GRID") == 0)
            {
                want_grid = true;
                want_evse = false;
            }
            else if (strcasecmp(source, "EVSE") == 0)
            {
                want_grid = false;
                want_evse = true;
            }
        }
    }

    cJSON *out = cJSON_CreateObject();
    cJSON *arr = cJSON_CreateArray();

    if (xSemaphoreTake(g_cache.mtx, pdMS_TO_TICKS(50)) == pdTRUE)
    {
        if (want_grid && g_cache.has_grid)
        {
            cJSON_AddItemToArray(arr, meter_event_to_json(&g_cache.last_grid, g_cache.ts_grid_us));
        }
        if (want_evse && g_cache.has_evse)
        {
            cJSON_AddItemToArray(arr, meter_event_to_json(&g_cache.last_evse, g_cache.ts_evse_us));
        }
        xSemaphoreGive(g_cache.mtx);
    }

    cJSON_AddItemToObject(out, "meters", arr);
    httpd_resp_set_type(req, "application/json");
    char *s = cJSON_PrintUnformatted(out);
    httpd_resp_sendstr(req, s ? s : "{\"meters\":[]}");
    if (s)
        free(s);
    cJSON_Delete(out);
    return ESP_OK;
}

// ---------- Registro ----------
void register_meters_data_handlers(httpd_handle_t server, void *ctx)
{
    // registra event handler para receber amostras
    ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY, meters_event_handler, NULL));

    // endpoint GET
    httpd_uri_t live = {
        .uri = "/api/v1/meters/live",
        .method = HTTP_GET,
        .handler = meters_live_get_handler,
        .user_ctx = ctx};
    httpd_register_uri_handler(server, &live);

    ESP_LOGD(TAG, "REST /api/v1/meters/live registrado");
}

// Função para recuperar as configurações dos contadores
static esp_err_t meters_config_get_handler(httpd_req_t *req)
{
    ESP_LOGD(TAG, "Received GET request for /api/v1/config/meters");

    httpd_resp_set_type(req, "application/json");

    cJSON *config = cJSON_CreateObject();

    // Recuperando as configurações dos contadores
    meter_type_t gridmeterType = meter_manager_grid_get_model();
    meter_type_t evsemeterType = meter_manager_evse_get_model();

    ESP_LOGD(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType));
    ESP_LOGD(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType));

    // Adicionando os tipos de contadores ao objeto JSON
    cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType));
    cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType));

    // Convertendo o objeto JSON para uma string
    const char *json_str = cJSON_Print(config);
    ESP_LOGD(TAG, "Returning meters config: %s", json_str);

    httpd_resp_sendstr(req, json_str);

    // Liberação da memória
    free((void *)json_str);
    cJSON_Delete(config);

    return ESP_OK;
}

// Função para atualizar as configurações dos contadores
static esp_err_t meters_config_post_handler(httpd_req_t *req)
{
    ESP_LOGD(TAG, "Received POST request for /api/v1/config/meters");

    char buf[512];
    int len = httpd_req_recv(req, buf, sizeof(buf) - 1);

    if (len <= 0)
    {
        ESP_LOGE(TAG, "Received empty body in POST request");
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
        return ESP_FAIL;
    }

    buf[len] = '\0'; // Garantir que a string está terminada

    ESP_LOGD(TAG, "Received POST data: %s", buf);

    cJSON *json = cJSON_Parse(buf);
    if (!json)
    {
        ESP_LOGE(TAG, "Failed to parse JSON data");
        // Resposta detalhada de erro
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format");
        return ESP_FAIL;
    }

    // Atualizando os contadores
    cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter");
    if (gridmeter)
    {
        meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type
        ESP_LOGD(TAG, "Updating grid meter type to: %s", gridmeter->valuestring);
        meter_manager_grid_set_model(gridType);
    }

    cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter");
    if (evsemeter)
    {
        meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type
        ESP_LOGD(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring);
        meter_manager_evse_set_model(evseType);
    }

    cJSON_Delete(json);
    httpd_resp_sendstr(req, "Meters updated successfully");

    ESP_LOGD(TAG, "Meters configuration updated successfully");

    return ESP_OK;
}

// Registrando os manipuladores de URI para os contadores
void register_meters_settings_handlers(httpd_handle_t server, void *ctx)
{
    ESP_LOGD(TAG, "Registering URI handlers for meters settings");

    // URI para o método GET
    httpd_uri_t meters_get_uri = {
        .uri = "/api/v1/config/meters",
        .method = HTTP_GET,
        .handler = meters_config_get_handler,
        .user_ctx = ctx};
    ESP_LOGD(TAG, "Registering GET handler for /api/v1/config/meters");
    httpd_register_uri_handler(server, &meters_get_uri);

    // URI para o método POST
    httpd_uri_t meters_post_uri = {
        .uri = "/api/v1/config/meters",
        .method = HTTP_POST,
        .handler = meters_config_post_handler,
        .user_ctx = ctx};
    ESP_LOGD(TAG, "Registering POST handler for /api/v1/config/meters");
    httpd_register_uri_handler(server, &meters_post_uri);
}
