// components/evse_link/src/evse_link.c
//
// Camada de transporte EVSE-Link:
// - carrega config (mode/self_id/enabled)
// - init do framing
// - task RX (UART -> framing)
// - entrega frames completos ao callback registado
//
// NOTA: a logica de protocolo (CMD_POLL / ACK / etc.) deve ficar em
// evse_link_master.c / evse_link_slave.c.

#include "evse_link.h"
#include "evse_link_framing.h"

#include "driver/uart.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include <stdbool.h>
#include <stdint.h>

#include "storage_service.h"

static const char *TAG = "evse_link";

#define _NVS_NAMESPACE "evse_link"
#define _KEY_MODE "mode"
#define _KEY_SELF_ID "self_id"
#define _KEY_ENABLED "enabled"

static evse_link_mode_t _mode = EVSE_LINK_MODE_MASTER;
static uint8_t _self_id = 0x01;
static bool _enabled = false;

static evse_link_rx_cb_t _rx_cb = NULL;
static bool s_evse_link_inited = false;

extern void evse_link_master_init(void);
extern void evse_link_slave_init(void);

static void framing_rx_cb(uint8_t src, uint8_t dest,
                          const uint8_t *payload, uint8_t len)
{
    if (_rx_cb)
        _rx_cb(src, dest, payload, len);
}

void evse_link_register_rx_cb(evse_link_rx_cb_t cb)
{
    _rx_cb = cb;
}

static void load_link_config(void)
{
    uint8_t u8 = 0;

    esp_err_t err = storage_get_u8_sync(_NVS_NAMESPACE, _KEY_MODE, &u8, pdMS_TO_TICKS(500));
    if (err == ESP_OK && (u8 == (uint8_t)EVSE_LINK_MODE_MASTER || u8 == (uint8_t)EVSE_LINK_MODE_SLAVE))
        _mode = (evse_link_mode_t)u8;
    else
    {
        _mode = EVSE_LINK_MODE_MASTER;
        (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_MODE, (uint8_t)_mode);
        ESP_LOGW(TAG, "Missing/invalid mode (%s) -> default MASTER", esp_err_to_name(err));
    }

    err = storage_get_u8_sync(_NVS_NAMESPACE, _KEY_SELF_ID, &u8, pdMS_TO_TICKS(500));
    if (err == ESP_OK)
        _self_id = u8;
    else
    {
        _self_id = 0x01;
        (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_SELF_ID, _self_id);
        ESP_LOGW(TAG, "Missing self_id (%s) -> default 0x%02X", esp_err_to_name(err), _self_id);
    }

    err = storage_get_u8_sync(_NVS_NAMESPACE, _KEY_ENABLED, &u8, pdMS_TO_TICKS(500));
    if (err == ESP_OK && u8 <= 1)
        _enabled = (u8 != 0);
    else
    {
        _enabled = false;
        (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_ENABLED, 0);
        ESP_LOGW(TAG, "Missing/invalid enabled (%s) -> default false", esp_err_to_name(err));
    }
}

static void save_link_config(void)
{
    (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_MODE, (uint8_t)_mode);
    (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_SELF_ID, _self_id);
    (void)storage_set_u8_async(_NVS_NAMESPACE, _KEY_ENABLED, _enabled ? 1 : 0);
}

void evse_link_set_mode(evse_link_mode_t m)
{
    if (m != EVSE_LINK_MODE_MASTER && m != EVSE_LINK_MODE_SLAVE)
    {
        ESP_LOGW(TAG, "Invalid link mode: %d", (int)m);
        return;
    }
    if (_mode == m)
        return;

    _mode = m;
    save_link_config();
}

evse_link_mode_t evse_link_get_mode(void) { return _mode; }

void evse_link_set_self_id(uint8_t id)
{
    if (_self_id == id)
        return;
    _self_id = id;
    save_link_config();
}

uint8_t evse_link_get_self_id(void) { return _self_id; }

void evse_link_set_enabled(bool en)
{
    if (_enabled == en)
        return;
    _enabled = en;
    save_link_config();
}

bool evse_link_is_enabled(void) { return _enabled; }

static void evse_link_rx_task(void *arg)
{
    (void)arg;
    ESP_LOGI(TAG, "evse_link_rx_task started");

    uint8_t buf[UART_RX_BUF_SIZE];

    while (true)
    {
        int len = uart_read_bytes(UART_PORT, buf, sizeof(buf), pdMS_TO_TICKS(1000));
        if (len > 0)
        {
            ESP_LOGD(TAG, "UART RX: len=%d first=0x%02X last=0x%02X", len, buf[0], buf[len - 1]);

            for (int i = 0; i < len; ++i)
                evse_link_recv_byte(buf[i]);
        }
    }
}

void evse_link_init(void)
{
    if (s_evse_link_inited)
    {
        ESP_LOGW(TAG, "evse_link_init called twice; ignoring");
        return;
    }
    s_evse_link_inited = true;

    esp_err_t se = storage_service_init();
    if (se == ESP_OK)
        load_link_config();
    else
        ESP_LOGE(TAG, "storage_service_init failed: %s (defaults in RAM)", esp_err_to_name(se));

    ESP_LOGI(TAG, "Link init: mode=%c id=0x%02X enabled=%d",
             _mode == EVSE_LINK_MODE_MASTER ? 'M' : 'S',
             _self_id, _enabled);

    if (!_enabled)
        return;

    evse_link_framing_init();
    evse_link_framing_register_cb(framing_rx_cb);

    if (xTaskCreate(evse_link_rx_task, "evse_link_rx", 4096, NULL, 4, NULL) != pdPASS)
    {
        ESP_LOGE(TAG, "Failed to create evse_link_rx task");
        return;
    }

    if (_mode == EVSE_LINK_MODE_MASTER)
        evse_link_master_init();
    else
        evse_link_slave_init();
}

bool evse_link_send(uint8_t dest, const uint8_t *payload, uint8_t len)
{
    if (!evse_link_is_enabled())
        return false;

    uint8_t src = evse_link_get_self_id();
    return evse_link_framing_send(dest, src, payload, len);
}

void evse_link_recv_byte(uint8_t byte)
{
    evse_link_framing_recv_byte(byte);
}
