#include "loadbalancing_settings_api.h"
#include "loadbalancer.h"

#include "esp_log.h"
#include "esp_err.h"
#include "esp_http_server.h"
#include "cJSON.h"

#include <stdlib.h>
#include <string.h>

static const char *TAG = "loadbalancing_settings_api";

// limites simples
#define MIN_GRID_A 6
#define MAX_GRID_A 100

#define MIN_PV_W 0
#define MAX_PV_W 100000 // ajusta se quiseres

static esp_err_t send_json(httpd_req_t *req, cJSON *root)
{
    httpd_resp_set_type(req, "application/json");

    char *json_str = cJSON_PrintUnformatted(root);
    if (!json_str)
    {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "JSON encode failed");
        return ESP_FAIL;
    }

    esp_err_t err = httpd_resp_sendstr(req, json_str);
    free(json_str);
    return err;
}

// GET -> payload novo
static esp_err_t loadbalancing_config_get_handler(httpd_req_t *req)
{
    cJSON *root = cJSON_CreateObject();
    if (!root)
    {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "No mem");
        return ESP_FAIL;
    }

    cJSON_AddBoolToObject(root, "enabled", loadbalancer_is_enabled());

    cJSON *grid = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "gridLimit", grid);
    cJSON_AddBoolToObject(grid, "enabled", loadbalancer_grid_is_enabled());
    cJSON_AddNumberToObject(grid, "maxImportA", loadbalancer_grid_get_max_import_a());

    cJSON *pv = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "pv", pv);
    cJSON_AddBoolToObject(pv, "enabled", loadbalancer_pv_is_enabled());
    cJSON_AddNumberToObject(pv, "maxImportW", loadbalancer_pv_get_max_import_w());

    esp_err_t err = send_json(req, root);
    cJSON_Delete(root);
    return err;
}

// lê body de forma robusta (httpd_req_recv pode devolver parcial)
static esp_err_t read_body(httpd_req_t *req, char *buf, size_t buf_sz)
{
    if (req->content_len <= 0)
        return ESP_FAIL;
    if ((size_t)req->content_len >= buf_sz)
        return ESP_ERR_NO_MEM;

    int remaining = req->content_len;
    int off = 0;

    while (remaining > 0)
    {
        int r = httpd_req_recv(req, buf + off, remaining);
        if (r <= 0)
            return ESP_FAIL;
        off += r;
        remaining -= r;
    }

    buf[off] = '\0';
    return ESP_OK;
}

// POST -> updates parciais aceites
static esp_err_t loadbalancing_config_post_handler(httpd_req_t *req)
{
    if (req->content_len <= 0)
    {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
        return ESP_FAIL;
    }

    if (req->content_len >= 512)
    {
        httpd_resp_send_err(req, HTTPD_413_CONTENT_TOO_LARGE, "Body too large");
        return ESP_FAIL;
    }

    char buf[512];
    esp_err_t rb = read_body(req, buf, sizeof(buf));
    if (rb == ESP_ERR_NO_MEM)
    {
        httpd_resp_send_err(req, HTTPD_413_CONTENT_TOO_LARGE, "Body too large");
        return ESP_FAIL;
    }
    if (rb != ESP_OK)
    {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Failed to read body");
        return ESP_FAIL;
    }

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

    cJSON *json = cJSON_Parse(buf);
    if (!json)
    {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
        return ESP_FAIL;
    }

    // enabled (top-level)
    cJSON *enabled_item = cJSON_GetObjectItem(json, "enabled");
    if (enabled_item && cJSON_IsBool(enabled_item))
    {
        loadbalancer_set_enabled(cJSON_IsTrue(enabled_item));
    }

    // gridLimit
    cJSON *grid = cJSON_GetObjectItem(json, "gridLimit");
    if (grid && cJSON_IsObject(grid))
    {
        cJSON *g_en = cJSON_GetObjectItem(grid, "enabled");
        if (g_en && cJSON_IsBool(g_en))
        {
            loadbalancer_grid_set_enabled(cJSON_IsTrue(g_en));
        }

        cJSON *g_maxA = cJSON_GetObjectItem(grid, "maxImportA");
        if (g_maxA && cJSON_IsNumber(g_maxA))
        {
            int maxA = (int)g_maxA->valuedouble;

            if (maxA < MIN_GRID_A || maxA > MAX_GRID_A)
            {
                cJSON_Delete(json);
                httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "gridLimit.maxImportA must be 6-100");
                return ESP_FAIL;
            }

            esp_err_t e = loadbalancer_grid_set_max_import_a((uint8_t)maxA);
            if (e != ESP_OK)
            {
                cJSON_Delete(json);
                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to set gridLimit.maxImportA");
                return ESP_FAIL;
            }
        }
    }

    // pv
    cJSON *pv = cJSON_GetObjectItem(json, "pv");
    if (pv && cJSON_IsObject(pv))
    {
        cJSON *p_en = cJSON_GetObjectItem(pv, "enabled");
        if (p_en && cJSON_IsBool(p_en))
        {
            loadbalancer_pv_set_enabled(cJSON_IsTrue(p_en));
        }

        cJSON *p_maxW = cJSON_GetObjectItem(pv, "maxImportW");
        if (p_maxW && cJSON_IsNumber(p_maxW))
        {
            int32_t maxW = (int32_t)p_maxW->valuedouble;

            if (maxW < MIN_PV_W || maxW > MAX_PV_W)
            {
                cJSON_Delete(json);
                httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "pv.maxImportW out of range");
                return ESP_FAIL;
            }

            esp_err_t e = loadbalancer_pv_set_max_import_w(maxW);
            if (e != ESP_OK)
            {
                cJSON_Delete(json);
                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to set pv.maxImportW");
                return ESP_FAIL;
            }
        }
    }

    cJSON_Delete(json);
    httpd_resp_sendstr(req, "OK");
    return ESP_OK;
}

void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx)
{
    httpd_uri_t get_uri = {
        .uri = "/api/v1/config/loadbalancing",
        .method = HTTP_GET,
        .handler = loadbalancing_config_get_handler,
        .user_ctx = ctx};
    httpd_register_uri_handler(server, &get_uri);

    httpd_uri_t post_uri = {
        .uri = "/api/v1/config/loadbalancing",
        .method = HTTP_POST,
        .handler = loadbalancing_config_post_handler,
        .user_ctx = ctx};
    httpd_register_uri_handler(server, &post_uri);
}
