#include "evse_fsm.h"
#include "evse_api.h"
#include "evse_pilot.h"
#include "evse_config.h"
#include "esp_log.h"
#include "ac_relay.h"
#include "board_config.h"
#include "socket_lock.h"
#include "proximity.h"
#include "rcm.h"
#include "evse_state.h"
#include "evse_error.h"

static const char *TAG = "evse_fsm";

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

static bool c1_d1_waiting = false;
static TickType_t c1_d1_relay_to = 0;

void evse_fsm_reset(void) {
    evse_set_state(EVSE_STATE_A);
    c1_d1_waiting = false;
    c1_d1_relay_to = 0;
}

// ... includes e defines como já estão

static void update_outputs(evse_state_t state) {
    const uint16_t current = evse_get_runtime_charging_current();
    uint8_t cable_max_current = evse_get_max_charging_current();
    const bool socket_outlet = evse_get_socket_outlet();

    if (socket_outlet) {
        cable_max_current = proximity_get_max_current();
    }

    // Segurança: relé sempre off e outputs seguros em caso de erro
    if (evse_get_error() != 0) {
        if (ac_relay_get_state()) {
            ac_relay_set_state(false);
            ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!");
        }
        ac_relay_set_state(false); // redundância tolerável
        pilot_set_level(true);     // sinal pilot sempre 12V (A)
        if (board_config.socket_lock && socket_outlet) {
            socket_lock_set_locked(false);
        }
        return;
    }

    // Fluxo normal
    switch (state) {
        case EVSE_STATE_A:
        case EVSE_STATE_E:
        case EVSE_STATE_F:
            ac_relay_set_state(false);
            pilot_set_level(state == EVSE_STATE_A);
            if (board_config.socket_lock && socket_outlet) {
                socket_lock_set_locked(false);
            }
            break;

        case EVSE_STATE_B1:
            pilot_set_level(true);
            ac_relay_set_state(false);
            if (board_config.socket_lock && socket_outlet) {
                socket_lock_set_locked(true);
            }

            if (rcm_test()) {
                //ESP_LOGI(TAG, "RCM self test passed");
            } else {
                //ESP_LOGW(TAG, "RCM self test failed");
            }
            break;

        case EVSE_STATE_B2:
            pilot_set_amps(MIN(current, cable_max_current));
            ac_relay_set_state(false);
            break;

        case EVSE_STATE_C1:
        case EVSE_STATE_D1:
            pilot_set_level(true);
            c1_d1_waiting = true;
            c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000);
            break;

        case EVSE_STATE_C2:
        case EVSE_STATE_D2:
            pilot_set_amps(MIN(current, cable_max_current));
            ac_relay_set_state(true); // Só chega aqui se não há erro!
            break;
    }
}

// FSM principal - centraliza a lógica de erro e de todos os estados
void evse_fsm_process(
    pilot_voltage_t pilot_voltage,
    bool authorized,
    bool available,
    bool enabled
) {
    // Proteção total: erro força F sempre!
    if (evse_get_error() != 0) {
        if (evse_get_state() != EVSE_STATE_F) {
            ESP_LOGW(TAG, "Erro ativo detectado: forçando estado FAULT (F)");
            evse_set_state(EVSE_STATE_F);
        }
        update_outputs(EVSE_STATE_F);
        return;
    }

    TickType_t now = xTaskGetTickCount();
    evse_state_t prev = evse_get_state();
    evse_state_t curr = prev;

    switch (curr) {
        case EVSE_STATE_A:
            if (!available) {
                evse_set_state(EVSE_STATE_F);
            } else if (pilot_voltage == PILOT_VOLTAGE_9) {
                evse_set_state(EVSE_STATE_B1);
            }
            break;

        case EVSE_STATE_B1:
        case EVSE_STATE_B2:
            if (!available) {
                evse_set_state(EVSE_STATE_F);
                break;
            }
            switch (pilot_voltage) {
                case PILOT_VOLTAGE_12:
                    evse_set_state(EVSE_STATE_A);
                    break;
                case PILOT_VOLTAGE_9:
                    evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
                    break;
                case PILOT_VOLTAGE_6:
                    evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
                    break;
                default:
                    break;
            }
            break;

        case EVSE_STATE_C1:
        case EVSE_STATE_D1:
            if (c1_d1_waiting && now >= c1_d1_relay_to) {
                ac_relay_set_state(false);
                c1_d1_waiting = false;
                if (!available) {
                    evse_set_state(EVSE_STATE_F);
                    break;
                }
            }
            __attribute__((fallthrough));

        case EVSE_STATE_C2:
        case EVSE_STATE_D2:
            if (!enabled || !available) {
                evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1)
                               ? EVSE_STATE_D1 : EVSE_STATE_C1);
                break;
            }
            switch (pilot_voltage) {
                case PILOT_VOLTAGE_6:
                    evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1);
                    break;
                case PILOT_VOLTAGE_3:
                    evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
                    break;
                case PILOT_VOLTAGE_9:
                    evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1);
                    break;
                case PILOT_VOLTAGE_12:
                    evse_set_state(EVSE_STATE_A);
                    break;
                default:
                    break;
            }
            break;

        case EVSE_STATE_E:
            // Estado elétrico grave: só reset manual
            break;

        case EVSE_STATE_F:
            // Fault: só sai se disponível e sem erro
            if (available && evse_get_error() == 0) {
                evse_set_state(EVSE_STATE_A);
            }
            break;
    }

    evse_state_t next = evse_get_state();
    update_outputs(next);

    if (next != prev) {
        ESP_LOGI(TAG, "State changed: %s -> %s",
                 evse_state_to_str(prev),
                 evse_state_to_str(next));
    }
}
