#include <string.h>
#include <stdbool.h>
#include <inttypes.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_log.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_spiffs.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/gpio.h"

#include "network.h"
#include "board_config.h"
#include "logger.h"
#include "rest_main.h" 

#include "peripherals.h"
#include "protocols.h"
#include "evse_manager.h"
#include "evse_core.h"
#include "auth.h"
#include "loadbalancer.h"
#include "meter_manager.h"
#include "buzzer.h"


#define EVSE_MANAGER_TICK_PERIOD_MS 1000
#define AP_CONNECTION_TIMEOUT        120000
#define RESET_HOLD_TIME              10000
#define DEBOUNCE_TIME_MS             50

#define PRESS_BIT                    BIT0
#define RELEASED_BIT                 BIT1

static const char *TAG = "app_main";

static TaskHandle_t user_input_task;
static TickType_t press_tick = 0;
static TickType_t last_interrupt_tick = 0;
static bool pressed = false;


//
// File system (SPIFFS) init and info
//
static void fs_info(esp_vfs_spiffs_conf_t *conf) {
    size_t total = 0, used = 0;
    esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used);
    if (ret == ESP_OK)
        ESP_LOGI(TAG, "Partition %s: total: %d, used: %d", conf->partition_label, total, used);
    else
        ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret));
}

static void fs_init(void) {
    esp_vfs_spiffs_conf_t cfg_conf = {
        .base_path = "/cfg",
        .partition_label = "cfg",
        .max_files = 1,
        .format_if_mount_failed = false
    };

    esp_vfs_spiffs_conf_t data_conf = {
        .base_path = "/data",
        .partition_label = "data",
        .max_files = 5,
        .format_if_mount_failed = true
    };

    ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf));
    ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf));

    fs_info(&cfg_conf);
    fs_info(&data_conf);
}
//
// Wi-Fi event monitoring task
//
static void wifi_event_task_func(void *param) {
    EventBits_t mode_bits;
    for (;;) {
        // Wait indefinitely until either AP or STA mode is entered
        mode_bits = xEventGroupWaitBits(
            wifi_event_group,
            WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT,
            pdFALSE,    // do not clear bits on exit
            pdFALSE,    // wait for any bit
            portMAX_DELAY
        );

        if (mode_bits & WIFI_AP_MODE_BIT) {
            // We're in AP mode: wait for a client to connect within the timeout
            if (xEventGroupWaitBits(
                    wifi_event_group,
                    WIFI_AP_CONNECTED_BIT,
                    pdFALSE,
                    pdFALSE,
                    pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)
                ) & WIFI_AP_CONNECTED_BIT) {
                // Once connected, block until the client disconnects
                xEventGroupWaitBits(
                    wifi_event_group,
                    WIFI_AP_DISCONNECTED_BIT,
                    pdFALSE,
                    pdFALSE,
                    portMAX_DELAY
                );
            } else {
                // Timeout expired with no client—optionally stop the AP
                if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) {
                    // wifi_ap_stop();
                }
            }
        } else if (mode_bits & WIFI_STA_MODE_BIT) {
            // We're in STA mode: block until disconnected from the AP
            xEventGroupWaitBits(
                wifi_event_group,
                WIFI_STA_DISCONNECTED_BIT,
                pdFALSE,
                pdFALSE,
                portMAX_DELAY
            );
        }

        // Prevent this task from hogging the CPU when idle
        //vTaskDelay(pdMS_TO_TICKS(10));
    }
}

//
// Button press handler
//
static void handle_button_press(void) {
    // If not already in AP mode, start it
    if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) {
        ESP_LOGI(TAG, "Starting Wi-Fi AP mode");
        wifi_ap_start();
    }
}

// Task to handle button press/release notifications
static void user_input_task_func(void *param) {
    uint32_t notification;
    for (;;) {
        // Wait for notification bits from ISR
        if (xTaskNotifyWait(
                0,           // do not clear any bits on entry
                UINT32_MAX,  // clear all bits on exit
                &notification,
                portMAX_DELAY)) {
            // Handle button press event
            if (notification & PRESS_BIT) {
                press_tick = xTaskGetTickCount();
                pressed = true;
                ESP_LOGI(TAG, "Button Pressed");
                handle_button_press();
            }
            // Handle button release event (only if previously pressed)
            if ((notification & RELEASED_BIT) && pressed) {
                pressed = false;
                ESP_LOGI(TAG, "Button Released");
                handle_button_press();
            }
        }
    }
}

// ISR for button GPIO interrupt (active-low)
static void IRAM_ATTR button_isr_handler(void *arg) {
    BaseType_t higher_task_woken = pdFALSE;
    TickType_t now = xTaskGetTickCountFromISR();

    // Debounce: ignore interrupts occurring too close together
    if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) {
        return;
    }
    last_interrupt_tick = now;

    // Read GPIO level: 0 = button pressed, 1 = button released
    int level = gpio_get_level(board_config.button_wifi_gpio);
    if (level == 0) {
        // Notify task: button pressed
        xTaskNotifyFromISR(
            user_input_task,
            PRESS_BIT,
            eSetBits,
            &higher_task_woken);
    } else {
        // Notify task: button released
        xTaskNotifyFromISR(
            user_input_task,
            RELEASED_BIT,
            eSetBits,
            &higher_task_woken);
    }

    // Yield to higher priority task if unblocked
    if (higher_task_woken) {
        portYIELD_FROM_ISR();
    }
}


static void button_init(void) {
    gpio_config_t conf = {
        .pin_bit_mask = BIT64(board_config.button_wifi_gpio),
        .mode = GPIO_MODE_INPUT,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .intr_type = GPIO_INTR_ANYEDGE
    };
    ESP_ERROR_CHECK(gpio_config(&conf));
    ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL));
}

//
// Inicialização dos módulos do sistema
//
static void init_modules(void) {
    peripherals_init();
    //api_init();
    buzzer_init();
    ESP_ERROR_CHECK(rest_server_init("/data"));
    protocols_init();
    evse_manager_init();
    evse_init();  // Cria a task para FSM
    button_init();
    auth_init();
    loadbalancer_init();
    meter_manager_init();
    meter_manager_start();    



    //wifi_ap_start();
    // Outros módulos (descomente conforme necessário)
    // meter_init();
    // ocpp_start();
    // orno_modbus_start();
    // currentshaper_start();
    // initWiegand();
    // meter_zigbee_start();
    // master_sync_start();
    // slave_sync_start();
}

//
// Função principal do firmware
//
void app_main(void) {
    logger_init();
    esp_log_set_vprintf(logger_vprintf);

    esp_reset_reason_t reason = esp_reset_reason();
    ESP_LOGI(TAG, "Reset reason: %d", reason);

    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGW(TAG, "Erasing NVS flash");
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    fs_init();
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(gpio_install_isr_service(0));

    board_config_load();
    wifi_ini();
    //wifi_ap_start();
    init_modules();

    xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL);
    xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task);
}
