#include "buzzer_events.h"
#include "evse_events.h"
#include "auth_events.h"
#include "network_events.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#define BUZZER_GPIO GPIO_NUM_27
static const char *TAG = "Buzzer";

typedef struct {
    uint16_t on_ms;
    uint16_t off_ms;
} buzzer_step_t;

// Padrões de buzzer
static const buzzer_step_t pattern_plugged[]     = {{100, 100}, {200, 0}};
static const buzzer_step_t pattern_unplugged[]   = {{150, 150}, {150, 150}, {150, 0}};
static const buzzer_step_t pattern_charging[]    = {{80, 150}, {100, 120}, {120, 100}, {140, 0}};
static const buzzer_step_t pattern_ap_start[]    = {{300, 150}, {300, 0}};
static const buzzer_step_t pattern_card_read[]   = {{50, 50}, {50, 0}};
static const buzzer_step_t pattern_card_add[]    = {{100, 100}, {100, 100}, {100, 0}};
static const buzzer_step_t pattern_card_denied[] = {{300, 100}, {300, 0}};

typedef struct {
    const buzzer_step_t *steps;
    size_t length;
} buzzer_pattern_t;

static const buzzer_pattern_t buzzer_patterns[] = {
    [BUZZER_PATTERN_PLUGGED]     = {pattern_plugged,     sizeof(pattern_plugged) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_UNPLUGGED]   = {pattern_unplugged,   sizeof(pattern_unplugged) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_CHARGING]    = {pattern_charging,    sizeof(pattern_charging) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_AP_START]    = {pattern_ap_start,    sizeof(pattern_ap_start) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_CARD_READ]   = {pattern_card_read,   sizeof(pattern_card_read) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_CARD_ADD]    = {pattern_card_add,    sizeof(pattern_card_add) / sizeof(buzzer_step_t)},
    [BUZZER_PATTERN_CARD_DENIED] = {pattern_card_denied, sizeof(pattern_card_denied) / sizeof(buzzer_step_t)},
};

static void buzzer_on(void)  { gpio_set_level(BUZZER_GPIO, 1); }
static void buzzer_off(void) { gpio_set_level(BUZZER_GPIO, 0); }

static void buzzer_execute(buzzer_pattern_id_t pattern_id) {
    if ((int)pattern_id <= BUZZER_PATTERN_NONE || pattern_id >= BUZZER_PATTERN_MAX) {
        ESP_LOGW(TAG, "Invalid buzzer pattern id: %d", pattern_id);
        return;
    }

    ESP_LOGD(TAG, "Executing buzzer pattern ID: %d", pattern_id);
    const buzzer_pattern_t *pattern = &buzzer_patterns[pattern_id];
    for (size_t i = 0; i < pattern->length; i++) {
        buzzer_on();
        vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].on_ms));
        buzzer_off();
        if (pattern->steps[i].off_ms > 0) {
            vTaskDelay(pdMS_TO_TICKS(pattern->steps[i].off_ms));
        }
    }
}

static void buzzer_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
    if (base != BUZZER_EVENTS || id != BUZZER_EVENT_PLAY_PATTERN || data == NULL) return;

    buzzer_event_data_t *evt = (buzzer_event_data_t *)data;
    if ((int)evt->pattern < BUZZER_PATTERN_NONE || evt->pattern >= BUZZER_PATTERN_MAX) {
        ESP_LOGW(TAG, "Invalid buzzer pattern received: %d", evt->pattern);
        return;
    }

    buzzer_execute(evt->pattern);
}

static void evse_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
    if (base != EVSE_EVENTS || id != EVSE_EVENT_STATE_CHANGED || data == NULL) return;

    const evse_state_event_data_t *evt = (const evse_state_event_data_t *)data;
    ESP_LOGD(TAG, "EVSE event received: state = %d", evt->state);

    buzzer_event_data_t buzzer_evt = {0};

    switch (evt->state) {
        case EVSE_STATE_EVENT_IDLE:
            buzzer_evt.pattern = BUZZER_PATTERN_UNPLUGGED;
            break;
        case EVSE_STATE_EVENT_WAITING:
            buzzer_evt.pattern = BUZZER_PATTERN_PLUGGED;
            break;
        case EVSE_STATE_EVENT_CHARGING:
            buzzer_evt.pattern = BUZZER_PATTERN_CHARGING;
            break;
        case EVSE_STATE_EVENT_FAULT:
        default:
            return;
    }

    esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}

static void network_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) {
    if (id == NETWORK_EVENT_AP_STARTED) {
        buzzer_event_data_t evt = {
            .pattern = BUZZER_PATTERN_AP_START
        };
        esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY);
    }
}


static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data) {
    if (base != AUTH_EVENTS || event_data == NULL) return;

    buzzer_event_data_t buzzer_evt = {0};

    if (id == AUTH_EVENT_TAG_PROCESSED) {
        const auth_tag_event_data_t *evt = (const auth_tag_event_data_t *)event_data;
        ESP_LOGD(TAG, "AUTH processed: tag=%s authorized=%d", evt->tag, evt->authorized);
        buzzer_evt.pattern = evt->authorized ? BUZZER_PATTERN_CARD_READ : BUZZER_PATTERN_CARD_DENIED;

    } else if (id == AUTH_EVENT_TAG_SAVED) {
        buzzer_evt.pattern = BUZZER_PATTERN_CARD_ADD;
    } else {
        return;
    }

    esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &buzzer_evt, sizeof(buzzer_evt), portMAX_DELAY);
}

void buzzer_init(void) {
    gpio_config_t io = {
        .pin_bit_mask = BIT64(BUZZER_GPIO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_down_en = 0,
        .pull_up_en = 0,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&io);
    buzzer_off();

    // Registro de handlers
    ESP_ERROR_CHECK(esp_event_handler_register(BUZZER_EVENTS,
                                               BUZZER_EVENT_PLAY_PATTERN,
                                               buzzer_event_handler,
                                               NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS,
                                               EVSE_EVENT_STATE_CHANGED,
                                               evse_event_handler,
                                               NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS,
                                               AUTH_EVENT_TAG_PROCESSED,
                                               auth_event_handler,
                                               NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS,
                                               AUTH_EVENT_TAG_SAVED,
                                               auth_event_handler,
                                               NULL));

    ESP_ERROR_CHECK(esp_event_handler_register(
        NETWORK_EVENTS,
        NETWORK_EVENT_AP_STARTED,
        network_event_handler,
        NULL
    ));


    ESP_LOGI(TAG, "Buzzer initialized on GPIO %d", BUZZER_GPIO);
}
