#include "wifi_manager.h"

#include <cstdio>
#include <cstring>

#include "board.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_wifi.h"

static const char *TAG = "WIFI";
static EventGroupHandle_t s_wifi_events;
static esp_event_handler_instance_t s_wifi_handler;
static esp_event_handler_instance_t s_ip_handler;

static void handle_wifi_event(void *, esp_event_base_t base, int32_t id, void *data)
{
    if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
        ESP_LOGI(TAG, "[WIFI] connecting...");
        esp_wifi_connect();
        return;
    }

    if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
        xEventGroupClearBits(s_wifi_events, WIFI_CONNECTED_BIT);
        board_set_wifi_status(false, nullptr);
        const auto *event = static_cast<wifi_event_sta_disconnected_t *>(data);
        ESP_LOGW(TAG, "[WIFI] disconnected reason=%u; reconnecting...", event ? event->reason : 0);
        esp_wifi_connect();
        return;
    }

    if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
        auto *event = static_cast<ip_event_got_ip_t *>(data);
        char ip[16] = {};
        snprintf(ip, sizeof(ip), IPSTR, IP2STR(&event->ip_info.ip));
        ESP_LOGI(TAG, "[WIFI] connected %s", ip);
        board_set_wifi_status(true, ip);
        xEventGroupSetBits(s_wifi_events, WIFI_CONNECTED_BIT);
    }
}

esp_err_t wifi_manager_start(void)
{
    if (strlen(CONFIG_DEVICE_LAB_WIFI_SSID) == 0) {
        ESP_LOGE(TAG, "[WIFI] SSID is empty; copy config/sdkconfig.defaults.example to config/sdkconfig.defaults.local");
        board_set_wifi_status(false, "not configured");
        return ESP_ERR_INVALID_STATE;
    }

    s_wifi_events = xEventGroupCreate();
    if (s_wifi_events == nullptr) {
        return ESP_ERR_NO_MEM;
    }

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t init = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&init));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        WIFI_EVENT, ESP_EVENT_ANY_ID, &handle_wifi_event, nullptr, &s_wifi_handler));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(
        IP_EVENT, IP_EVENT_STA_GOT_IP, &handle_wifi_event, nullptr, &s_ip_handler));

    wifi_config_t config = {};
    strlcpy(reinterpret_cast<char *>(config.sta.ssid), CONFIG_DEVICE_LAB_WIFI_SSID,
            sizeof(config.sta.ssid));
    strlcpy(reinterpret_cast<char *>(config.sta.password), CONFIG_DEVICE_LAB_WIFI_PASSWORD,
            sizeof(config.sta.password));
    config.sta.threshold.authmode = strlen(CONFIG_DEVICE_LAB_WIFI_PASSWORD) == 0
        ? WIFI_AUTH_OPEN
        : WIFI_AUTH_WPA2_PSK;
    config.sta.pmf_cfg.capable = true;
    config.sta.pmf_cfg.required = false;

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
    ESP_ERROR_CHECK(esp_wifi_start());
    return ESP_OK;
}

EventGroupHandle_t wifi_manager_event_group(void)
{
    return s_wifi_events;
}
