#include "meter_zigbee.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_system.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "meter_events.h"

#define TAG "meter_zigbee"

// UART config
#define UART_PORT UART_NUM_2
#define TXD_PIN GPIO_NUM_17
#define RXD_PIN GPIO_NUM_16
#define UART_BUF_SIZE 128
#define RX_FRAME_SIZE 14

// Zigbee Attribute IDs
#define ATTR_CURRENT_L1 0x0006
#define ATTR_CURRENT_L2 0x0007
#define ATTR_CURRENT_L3 0x0008
#define ATTR_VOLTAGE_L1 0x0266
#define ATTR_CURRENT_L1_ALT 0x0267
#define ATTR_POWER_L1 0x0268
#define ATTR_VOLTAGE_L2 0x0269
#define ATTR_CURRENT_L2_ALT 0x026A
#define ATTR_POWER_L2 0x026B
#define ATTR_VOLTAGE_L3 0x026C
#define ATTR_CURRENT_L3_ALT 0x026D
#define ATTR_POWER_L3 0x026E
#define ATTR_FREQUENCY 0x0265
#define ATTR_POWER_FACTOR 0x020F
#define ATTR_TOTAL_ENERGY 0x0201

#define PHASE_COUNT 3
#define PHASE_L1 0
#define PHASE_L2 1
#define PHASE_L3 2

// Internal meter state
typedef struct
{
    float vrms[PHASE_COUNT];
    float irms[PHASE_COUNT];
    int watt[PHASE_COUNT];
    int var[PHASE_COUNT];
    int va[PHASE_COUNT];
    float frequency;
    float power_factor;
    float total_energy;
} meter_zigbee_data_t;

static bool phase_updated[PHASE_COUNT] = {false, false, false};

static meter_zigbee_data_t meter_data = {0};
static SemaphoreHandle_t meter_mutex = NULL;
static TaskHandle_t meter_zigbee_task = NULL;

bool meter_zigbee_is_running(void)
{
    return meter_zigbee_task != NULL;
}

static inline int32_t tuya_power16_to_signed(uint16_t p)
{
    // Igual ao quirk multi_dp_to_power()
    if (p > 0x7FFF)
    {
        return (int32_t)((0x999A - p) * -1);
    }
    return (int32_t)p;
}

static void meter_zigbee_post_event(void)
{
    meter_event_data_t evt = {
        .source = "GRID",
        .frequency = meter_data.frequency,
        .power_factor = meter_data.power_factor,
        .total_energy = meter_data.total_energy};

    memcpy(evt.vrms, meter_data.vrms, sizeof(evt.vrms));
    memcpy(evt.irms, meter_data.irms, sizeof(evt.irms));
    memcpy(evt.watt, meter_data.watt, sizeof(evt.watt));

    esp_err_t err = esp_event_post(METER_EVENT,
                                   METER_EVENT_DATA_READY,
                                   &evt,
                                   sizeof(evt),
                                   portMAX_DELAY);

    if (err != ESP_OK)
    {
        ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
    }
}

static void handle_zigbee_frame(const uint8_t *buf, size_t len)
{
    ESP_LOGD(TAG, "Received UART frame (%d bytes):", len);
    // ESP_LOG_BUFFER_HEX(TAG, buf, len);

    if (len < RX_FRAME_SIZE)
    {
        ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len);
        return;
    }

    uint16_t attr = buf[2] | (buf[3] << 8);
    uint8_t size = buf[5];

    if (size != 8)
    {
        ESP_LOGW(TAG, "Unsupported payload size: %d", size);
        return;
    }

    // payload 8 bytes começa em buf[6]
    const uint8_t *p = &buf[6];

    uint16_t volt_raw = ((uint16_t)p[0] << 8) | p[1];

    uint16_t curr_raw_u16 = ((uint16_t)p[3] << 8) | p[4]; // 2 bytes
    uint16_t pow_raw_u16 = ((uint16_t)p[6] << 8) | p[7];  // 2 bytes

    int32_t power = tuya_power16_to_signed(pow_raw_u16);

    float volt = volt_raw / 10.0f;
    float curr = curr_raw_u16 / 1000.0f;

    // Se queres “corrente com sinal”, deriva pelo sinal da potência:
    float current = (power < 0) ? -curr : curr;

    ESP_LOGD(TAG, "Attr 0x%04X: V=%.1fV I=%.3fA (signed=%+.3fA) P=%+ldW",
             attr, volt, curr, current, (long)power);

    if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE)
    {
        switch (attr)
        {
        case ATTR_CURRENT_L1:
        case ATTR_CURRENT_L1_ALT:
            meter_data.irms[PHASE_L1] = current;
            meter_data.vrms[PHASE_L1] = volt;
            meter_data.watt[PHASE_L1] = (int)power;
            phase_updated[PHASE_L1] = true;
            break;
        case ATTR_CURRENT_L2:
        case ATTR_CURRENT_L2_ALT:
            meter_data.irms[PHASE_L2] = current;
            meter_data.vrms[PHASE_L2] = volt;
            meter_data.watt[PHASE_L2] = (int)power;
            phase_updated[PHASE_L2] = true;
            break;
        case ATTR_CURRENT_L3:
        case ATTR_CURRENT_L3_ALT:
            meter_data.irms[PHASE_L3] = current;
            meter_data.vrms[PHASE_L3] = volt;
            meter_data.watt[PHASE_L3] = (int)power;
            phase_updated[PHASE_L3] = true;
            break;
        case ATTR_POWER_FACTOR:
            meter_data.power_factor = 0;
            break;
        case ATTR_FREQUENCY:
            meter_data.frequency = 0;
            break;
        case ATTR_TOTAL_ENERGY:
            meter_data.total_energy = 0;
            break;
        default:
            ESP_LOGW(TAG, "Unknown attr: 0x%04X", attr);
            break;
        }
        xSemaphoreGive(meter_mutex);
    }

    // Verifica se todas as 3 fases foram atualizadas
    if (phase_updated[PHASE_L1] /*&& phase_updated[PHASE_L2] && phase_updated[PHASE_L3]*/)
    {
        meter_zigbee_post_event();
        memset(phase_updated, 0, sizeof(phase_updated));
    }
}

static void meter_zigbee_task_func(void *param)
{
    uint8_t *buf = malloc(RX_FRAME_SIZE);
    if (!buf)
    {
        ESP_LOGE(TAG, "Failed to allocate buffer");
        vTaskDelete(NULL);
        return;
    }

    ESP_LOGI(TAG, "Zigbee meter task started");

    while (1)
    {
        int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(5000));
        if (len == RX_FRAME_SIZE)
        {
            handle_zigbee_frame(buf, len);
        }
        else if (len == 0)
        {
            ESP_LOGD(TAG, "UART timeout with no data");
        }
        else
        {
            ESP_LOGW(TAG, "Incomplete frame received (%d bytes)", len);
        }
    }

    free(buf);
    vTaskDelete(NULL);
}

esp_err_t meter_zigbee_init(void)
{
    ESP_LOGI(TAG, "Initializing Zigbee meter");

    if (!meter_mutex)
    {
        meter_mutex = xSemaphoreCreateMutex();
        if (!meter_mutex)
            return ESP_ERR_NO_MEM;
    }

    uart_config_t config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT};

    ESP_ERROR_CHECK(uart_param_config(UART_PORT, &config));
    ESP_ERROR_CHECK(uart_set_pin(UART_PORT, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_BUF_SIZE * 2, 0, 0, NULL, 0));

    return ESP_OK;
}

esp_err_t meter_zigbee_start(void)
{
    if (meter_zigbee_task)
        return ESP_ERR_INVALID_STATE;

    xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 3, &meter_zigbee_task);
    return ESP_OK;
}

void meter_zigbee_stop(void)
{

    if (meter_zigbee_task)
    {
        vTaskDelete(meter_zigbee_task);
        meter_zigbee_task = NULL;
    }

    uart_driver_delete(UART_PORT);

    if (meter_mutex)
    {
        vSemaphoreDelete(meter_mutex);
        meter_mutex = NULL;
    }
}