#include "evse_api.h"
#include "evse_state.h"
#include "evse_session.h"
#include "evse_events.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_log.h"
#include "esp_event.h"

#define EVSE_EVENT_POST_TIMEOUT_MS 50

static evse_state_t current_state = EVSE_STATE_A;
static bool is_authorized = false;

static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED;
static const char *TAG = "evse_state";

static evse_state_event_t map_state_to_event(evse_state_t s)
{
    switch (s)
    {
    case EVSE_STATE_A:
        return EVSE_STATE_EVENT_IDLE;

    case EVSE_STATE_B1:
    case EVSE_STATE_B2:
        return EVSE_STATE_EVENT_WAITING;

    case EVSE_STATE_C1:
    case EVSE_STATE_C2:
    case EVSE_STATE_D1:
    case EVSE_STATE_D2:
        return EVSE_STATE_EVENT_CHARGING;

    case EVSE_STATE_E:
    case EVSE_STATE_F:
        return EVSE_STATE_EVENT_FAULT;

    default:
        return EVSE_STATE_EVENT_IDLE;
    }
}

static void post_evse_event(evse_event_id_t id, const void *data, size_t len)
{
    esp_err_t err = esp_event_post(
        EVSE_EVENTS,
        id,
        data,
        len,
        portMAX_DELAY);

    if (err != ESP_OK)
    {
        ESP_LOGW(TAG, "esp_event_post(id=%d) failed: %s", (int)id, esp_err_to_name(err));
    }
}

bool evse_state_is_charging(evse_state_t state)
{
    // “charging” == energia efetiva (relé ON)
    return (state == EVSE_STATE_C2 || state == EVSE_STATE_D2);
}

bool evse_state_is_power_flowing(evse_state_t state)
{
    return evse_state_is_charging(state);
}

bool evse_state_is_requesting(evse_state_t state)
{
    // EV pediu carga mas o relé ainda está OFF
    return (state == EVSE_STATE_C1 || state == EVSE_STATE_D1);
}

bool evse_state_is_plugged(evse_state_t state)
{
    return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 ||
           state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
           state == EVSE_STATE_D1 || state == EVSE_STATE_D2;
}

bool evse_state_is_session(evse_state_t state)
{
    // Sessão lógica: “autorizado/pronto” ou “a pedir/a fornecer energia”
    return (state == EVSE_STATE_B2 ||
            state == EVSE_STATE_C1 || state == EVSE_STATE_C2 ||
            state == EVSE_STATE_D1 || state == EVSE_STATE_D2);
}

void evse_set_state(evse_state_t new_state)
{
    bool changed = false;
    evse_state_t prev_state;
    bool start_session = false;
    bool end_session = false;

    portENTER_CRITICAL(&state_mux);
    prev_state = current_state;

    if (new_state != current_state)
    {

        // Sessão começa quando entra em energia (relé ON)
        if (evse_state_is_power_flowing(new_state) && !evse_state_is_power_flowing(prev_state))
        {
            start_session = true;
        }
        // Sessão termina quando sai de energia
        else if (!evse_state_is_power_flowing(new_state) && evse_state_is_power_flowing(prev_state))
        {
            end_session = true;
        }

        current_state = new_state;
        changed = true;
    }
    portEXIT_CRITICAL(&state_mux);

    // Fora da região crítica
    if (start_session)
    {
        evse_session_start();
    }
    if (end_session)
    {
        evse_session_end();
    }

    if (changed)
    {
        ESP_LOGI(TAG, "State changed: %s → %s",
                 evse_state_to_str(prev_state),
                 evse_state_to_str(new_state));

        evse_state_event_data_t evt = {
            .state = map_state_to_event(new_state)};
        post_evse_event(EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt));
    }
}

evse_state_t evse_get_state(void)
{
    portENTER_CRITICAL(&state_mux);
    evse_state_t s = current_state;
    portEXIT_CRITICAL(&state_mux);
    return s;
}

const char *evse_state_to_str(evse_state_t state)
{
    switch (state)
    {
    case EVSE_STATE_A:
        return "A - EV Not Connected (12V)";
    case EVSE_STATE_B1:
        return "B1 - EV Connected (9V, Not Authorized)";
    case EVSE_STATE_B2:
        return "B2 - EV Connected (9V, Authorized and Ready)";
    case EVSE_STATE_C1:
        return "C1 - Charging Requested (6V, Relay Off)";
    case EVSE_STATE_C2:
        return "C2 - Charging Active (6V, Relay On)";
    case EVSE_STATE_D1:
        return "D1 - Ventilation Required (3V, Relay Off)";
    case EVSE_STATE_D2:
        return "D2 - Ventilation Active (3V, Relay On)";
    case EVSE_STATE_E:
        return "E - Error: Control Pilot Shorted to Ground (0V)";
    case EVSE_STATE_F:
        return "F - Fault: EVSE Unavailable or No Pilot Signal";
    default:
        return "Unknown State";
    }
}

void evse_state_init(void)
{
    portENTER_CRITICAL(&state_mux);
    current_state = EVSE_STATE_A;
    is_authorized = false;
    portEXIT_CRITICAL(&state_mux);

    ESP_LOGI(TAG, "Initialized in state: %s", evse_state_to_str(current_state));

    evse_state_event_data_t evt = {
        .state = map_state_to_event(current_state)};
    post_evse_event(EVSE_EVENT_INIT, &evt, sizeof(evt));
}

void evse_state_tick(void)
{
    // placeholder
}

void evse_state_set_authorized(bool authorized)
{
    portENTER_CRITICAL(&state_mux);
    is_authorized = authorized;
    portEXIT_CRITICAL(&state_mux);
}

bool evse_state_get_authorized(void)
{
    portENTER_CRITICAL(&state_mux);
    bool result = is_authorized;
    portEXIT_CRITICAL(&state_mux);
    return result;
}
