// components/evse/evse_fsm.c
#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

void evse_fsm_reset(void)
{
    evse_set_state(EVSE_STATE_A);
}

/**
 * @brief Atualiza saídas de hardware (pilot, relé, trava) em função do estado lógico.
 */
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 total: qualquer erro ativo força saída segura
    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!");
        }
        else
        {
            ac_relay_set_state(false);
        }

        // Em erro, garantir pilot OFF (não PWM / não +12V)
        pilot_set_level(true);

        if (board_config.socket_lock && socket_outlet)
        {
            socket_lock_set_locked(false);
        }
        return;
    }

    switch (state)
    {
    case EVSE_STATE_A:
    case EVSE_STATE_E:
    case EVSE_STATE_F:
        ac_relay_set_state(false);

        // A → pilot alto (+12V), E/F → pilot OFF
        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);
        }

        (void)rcm_test();
        break;

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

        if (board_config.socket_lock && socket_outlet)
        {
            socket_lock_set_locked(true);
        }
        break;

    case EVSE_STATE_C1:
    case EVSE_STATE_D1:
        pilot_set_amps(MIN(current, cable_max_current));
        ac_relay_set_state(false);

        if (board_config.socket_lock && socket_outlet)
        {
            socket_lock_set_locked(true);
        }
        break;

    case EVSE_STATE_C2:
    case EVSE_STATE_D2:
        pilot_set_amps(MIN(current, cable_max_current));
        ac_relay_set_state(true);

        if (board_config.socket_lock && socket_outlet)
        {
            socket_lock_set_locked(true);
        }
        break;
    }
}

/**
 * @brief Máquina de estados principal do EVSE (IEC 61851).
 */
void evse_fsm_process(
    pilot_voltage_t pilot_voltage,
    bool authorized,
    bool available,
    bool enabled)
{
    // 1) Erros globais: dominam qualquer outra lógica
    uint32_t err_bits = evse_get_error();
    if (err_bits != 0)
    {
        evse_state_t forced_state =
            (err_bits & EVSE_ERR_PILOT_FAULT_BIT) ? EVSE_STATE_E : EVSE_STATE_F;

        if (evse_get_state() != forced_state)
        {
            ESP_LOGW(TAG, "Erro ativo detectado: forçando estado %s",
                     evse_state_to_str(forced_state));
            evse_set_state(forced_state);
        }

        update_outputs(forced_state);
        return;
    }

    evse_state_t curr = evse_get_state();

    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;

        case PILOT_VOLTAGE_3:
            evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1);
            break;

        default:
            break;
        }
        break;

    case EVSE_STATE_C1:
    case EVSE_STATE_D1:
    case EVSE_STATE_C2:
    case EVSE_STATE_D2:
        if (!available)
        {
            evse_set_state(EVSE_STATE_F);
            break;
        }

        if (!enabled)
        {
            if (curr == EVSE_STATE_C2)
            {
                evse_set_state(EVSE_STATE_C1);
            }
            else if (curr == EVSE_STATE_D2)
            {
                evse_set_state(EVSE_STATE_D1);
            }
            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:
        // ✅ Agora recupera como F: se disponível e sem erro -> volta a A
        if (available && evse_get_error() == 0)
        {
            evse_set_state(EVSE_STATE_A);
        }
        break;

    case EVSE_STATE_F:
        if (available && evse_get_error() == 0)
        {
            evse_set_state(EVSE_STATE_A);
        }
        break;
    }

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