#include "grid_limiter.h"
#include "esp_log.h"
#include <math.h>

static const char *TAG = "grid_limiter";

#define DEFAULT_VOLTAGE_V (230.0f)

typedef struct
{
    bool enabled;
    uint8_t max_import_a;
} grid_cfg_t;

static grid_cfg_t s_cfg = {
    .enabled = false,
    .max_import_a = 32};

static float clamp_pf(float pf)
{
    if (pf < 0.05f || pf > 1.2f)
        return 1.0f;
    return pf;
}

static void estimate_v_and_phases(const meter_event_data_t *m, float *v_avg, int *nph)
{
    float sum = 0.0f;
    int cnt = 0;

    if (!m)
    {
        *v_avg = DEFAULT_VOLTAGE_V;
        *nph = 1;
        return;
    }

    for (int i = 0; i < 3; i++)
    {
        if (m->vrms[i] > 80.0f)
        {
            sum += m->vrms[i];
            cnt++;
        }
    }

    if (cnt == 0)
    {
        *v_avg = DEFAULT_VOLTAGE_V;
        *nph = 1;
        return;
    }

    *v_avg = sum / (float)cnt;
    *nph = cnt;
}

void grid_limiter_init(void) { /* nada */ }

void grid_limiter_set_enabled(bool en) { s_cfg.enabled = en; }
bool grid_limiter_is_enabled(void) { return s_cfg.enabled; }

esp_err_t grid_limiter_set_max_import_a(uint8_t a)
{
    if (a < 6 || a > 100)
        return ESP_ERR_INVALID_ARG;
    s_cfg.max_import_a = a;
    return ESP_OK;
}

uint8_t grid_limiter_get_max_import_a(void) { return s_cfg.max_import_a; }

float grid_limiter_limit_total_a(const meter_event_data_t *grid_evt,
                                 float fallback_grid_current_a,
                                 float current_total_a)
{
    if (!s_cfg.enabled)
        return current_total_a;
    if (current_total_a <= 0.0f)
        return 0.0f;

    float i_import = 0.0f;

    if (grid_evt && grid_evt->watt_total > 0)
    {
        float v_avg;
        int nph;
        estimate_v_and_phases(grid_evt, &v_avg, &nph);
        const float pf = clamp_pf(grid_evt->power_factor);
        const float denom = v_avg * (float)nph * pf;

        if (denom > 10.0f)
        {
            i_import = ((float)grid_evt->watt_total) / denom;
        }
        else
        {
            i_import = fallback_grid_current_a;
        }
    }
    else
    {
        // export (<=0) => import=0; ou sem potência => fallback
        if (grid_evt && grid_evt->watt_total < 0)
            i_import = 0.0f;
        else
            i_import = fallback_grid_current_a;
    }

    if (i_import <= (float)s_cfg.max_import_a + 0.01f)
        return current_total_a;

    const float over = i_import - (float)s_cfg.max_import_a;
    const float cut_a = ceilf(over); // conservador
    float new_total = current_total_a - cut_a;
    if (new_total < 0.0f)
        new_total = 0.0f;

    ESP_LOGD(TAG, "cap: i_import=%.2fA max=%uA over=%.2fA total=%.1fA -> %.1fA",
             i_import, (unsigned)s_cfg.max_import_a, over, current_total_a, new_total);

    return new_total;
}
