// components/evse/evse_core.c
#include "evse_fsm.h"
#include "evse_error.h"
#include "evse_limits.h"
#include "evse_config.h"
#include "evse_api.h"
#include "evse_session.h"
#include "evse_pilot.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"

static const char *TAG = "evse_core";

static SemaphoreHandle_t mutex;
static evse_state_t last_state = EVSE_STATE_A;

// Filtro simples de histerese no pilot
#define PILOT_STABLE_SAMPLES 2

static pilot_voltage_t s_last_raw = PILOT_VOLTAGE_12;
static pilot_voltage_t s_filtered = PILOT_VOLTAGE_12;
static int s_stable_count = 0;

static pilot_voltage_t filter_pilot_voltage(pilot_voltage_t raw)
{
    if (raw == s_last_raw)
    {
        if (s_stable_count < PILOT_STABLE_SAMPLES)
        {
            s_stable_count++;
        }
    }
    else
    {
        s_last_raw = raw;
        s_stable_count = 1;
    }

    if (s_stable_count >= PILOT_STABLE_SAMPLES && raw != s_filtered)
    {
        s_filtered = raw;
    }

    return s_filtered;
}

static void evse_process(void);
static void evse_core_task(void *arg);

void evse_init(void)
{
    ESP_LOGI(TAG, "EVSE Init");

    mutex = xSemaphoreCreateMutex();
    if (!mutex)
    {
        ESP_LOGE(TAG, "Failed to create EVSE core mutex");
        return;
    }

    evse_check_defaults();
    evse_fsm_reset();
    pilot_set_level(true);

    BaseType_t rc = xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 6, NULL);
    configASSERT(rc == pdPASS);
}

static void evse_process(void)
{
    if (!mutex)
    {
        return;
    }

    xSemaphoreTake(mutex, portMAX_DELAY);

    pilot_voltage_t pilot_raw;
    bool is_n12v = false;

    pilot_measure(&pilot_raw, &is_n12v);
    pilot_voltage_t pilot_voltage = filter_pilot_voltage(pilot_raw);

    ESP_LOGD(TAG, "Pilot(raw=%d, filt=%d), -12V: %s",
             pilot_raw, pilot_voltage, is_n12v ? "yes" : "no");

    // raw set/clear; erro visível mantém holdoff interno (60s após sumir)
    evse_error_check(pilot_voltage, is_n12v);

    // ✅ Sem cooldown externo: disponibilidade depende só do erro "visível"
    bool available = evse_config_is_available() && (evse_get_error() == 0);
    bool enabled = evse_config_is_enabled();

    evse_fsm_process(
        pilot_voltage,
        evse_state_get_authorized(),
        available,
        enabled);

    evse_limits_check();

    if (evse_is_limit_reached())
    {
        if (evse_state_get_authorized())
        {
            ESP_LOGW(TAG, "Charging limit reached → revoking authorization");
            evse_session_clear_authorized_tag();
            evse_state_set_authorized(false);
        }
    }

    evse_state_t current = evse_get_state();
    if (current != last_state)
    {
        last_state = current;
    }

    xSemaphoreGive(mutex);
}

static void evse_core_task(void *arg)
{
    (void)arg;
    while (true)
    {
        evse_process();
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}
