#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"

// =========================
// Internal State Variables
// =========================

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";

// =========================
// Internal Mapping
// =========================

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: return EVSE_STATE_EVENT_WAITING;
        case EVSE_STATE_B2:
        case EVSE_STATE_C1:
        case EVSE_STATE_C2: return EVSE_STATE_EVENT_CHARGING;
        case EVSE_STATE_E:
        case EVSE_STATE_F:  return EVSE_STATE_EVENT_FAULT;
        default:            return EVSE_STATE_EVENT_IDLE;
    }
}

// =========================
// Public API
// =========================

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;

    // 1) Detecta transição de estado dentro da região crítica
    portENTER_CRITICAL(&state_mux);
      prev_state = current_state;
      if (new_state != current_state) {
          // se entrou em charging pela primeira vez
          if (evse_state_is_charging(new_state) && !evse_state_is_charging(prev_state)) {
              start_session = true;
          }
          // se saiu de charging para qualquer outro
          else if (!evse_state_is_charging(new_state) && evse_state_is_charging(prev_state)) {
              end_session = true;
          }
          current_state = new_state;
          changed = true;
      }
    portEXIT_CRITICAL(&state_mux);

    // 2) Executa start/end de sessão FORA da região crítica, evitando logs/alloc dentro dela
    if (start_session) {
        evse_session_start();
    }
    if (end_session) {
        evse_session_end();
    }

    // 3) Se mudou o estado, faz log e dispara evento
    if (changed) {
        const char *prev_str = evse_state_to_str(prev_state);
        const char *curr_str = evse_state_to_str(new_state);
        ESP_LOGI(TAG, "State changed: %s → %s", prev_str, curr_str);

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



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 = true;
    portEXIT_CRITICAL(&state_mux);

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

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

void evse_state_tick(void) {
    // Placeholder for future state logic
}

bool evse_state_is_charging(evse_state_t state) {
    return state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
}

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) {
    return state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2;
}

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;
}
