#include "wifi_manager.h"
#include "wifi_profile_selection.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 WifiProfileSelection s_profiles(CONFIG_DEVICE_LAB_WIFI_SECONDARY_SSID[0] != '\0');

static esp_err_t profile_config(unsigned profile, wifi_config_t &config)
{
    const char *ssid = profile == 0 ? CONFIG_DEVICE_LAB_WIFI_SSID : CONFIG_DEVICE_LAB_WIFI_SECONDARY_SSID;
    const char *password = profile == 0 ? CONFIG_DEVICE_LAB_WIFI_PASSWORD : CONFIG_DEVICE_LAB_WIFI_SECONDARY_PASSWORD;
    config = {};
    if (strlen(ssid) == 0 || strlen(ssid) > sizeof(config.sta.ssid) ||
        strlen(password) > sizeof(config.sta.password)) {
        ESP_LOGE(TAG, "[WIFI] invalid credential lengths profile=%u", profile + 1);
        return ESP_ERR_INVALID_ARG;
    }
    memcpy(config.sta.ssid, ssid, strlen(ssid));
    memcpy(config.sta.password, password, strlen(password));
    config.sta.threshold.authmode = strlen(password) == 0 ? WIFI_AUTH_OPEN : WIFI_AUTH_WPA2_PSK;
    config.sta.pmf_cfg.capable = true;
    config.sta.pmf_cfg.required = false;
    return ESP_OK;
}

static esp_err_t configure_profile(unsigned profile)
{
    wifi_config_t config = {};
    const esp_err_t result = profile_config(profile, config);
    if (result != ESP_OK) return result;
    return esp_wifi_set_config(WIFI_IF_STA, &config);
}

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 profile=%u", s_profiles.current() + 1);
        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);
        const unsigned previous = s_profiles.current();
        const unsigned next = s_profiles.disconnected();
        if (next != previous) ESP_ERROR_CHECK(configure_profile(next));
        ESP_LOGW(TAG, "[WIFI] disconnected reason=%u; reconnecting profile=%u switched=%d",
                 event->reason, next + 1, next != previous);
        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));
        s_profiles.got_ip();
        ESP_LOGI(TAG, "[WIFI] connected %s profile=%u", ip, s_profiles.current() + 1);
        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;
    }
    // Validate both configured profiles before registering asynchronous events.
    wifi_config_t checked = {};
    if (profile_config(0, checked) != ESP_OK ||
        (CONFIG_DEVICE_LAB_WIFI_SECONDARY_SSID[0] != '\0' && profile_config(1, checked) != ESP_OK)) {
        board_set_wifi_status(false, "invalid config");
        return ESP_ERR_INVALID_ARG;
    }

    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));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // Credentials are compiled from local defaults. Avoid repeated flash writes
    // while alternating unavailable profiles; no radio/transport setting changes.
    if (CONFIG_DEVICE_LAB_WIFI_SECONDARY_SSID[0] != '\0')
        ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(configure_profile(s_profiles.current()));
    ESP_ERROR_CHECK(esp_wifi_start());
    // Isolated speech receive A/B: keep every other transport setting unchanged.
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
    ESP_LOGI(TAG, "[WIFI] speech A/B power save disabled ps=%d", WIFI_PS_NONE);
    return ESP_OK;
}

EventGroupHandle_t wifi_manager_event_group(void)
{
    return s_wifi_events;
}
