#include "auth.h"
#include "auth_events.h"
#include "esp_event.h"
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <esp_log.h>
#include <string.h>
#include "wiegand_reader.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_random.h"


#define MAX_TAGS 50

static const char *TAG = "Auth";

static bool enabled = false;
static bool waiting_for_registration = false;
static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN];
static int tag_count = 0;

// NVS keys
#define NVS_NAMESPACE        "auth"
#define NVS_TAG_PREFIX       "tag_"
#define NVS_TAG_COUNT_KEY    "count"
#define NVS_ENABLED_KEY      "enabled"

// ===========================
// NVS Persistence
// ===========================

static void load_auth_config(void) {
    nvs_handle_t handle;
    esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
    if (err == ESP_OK) {
        uint8_t val;
        if (nvs_get_u8(handle, NVS_ENABLED_KEY, &val) == ESP_OK) {
            enabled = val;
            ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled);
        }
        nvs_close(handle);
    } else {
        ESP_LOGW(TAG, "No stored auth config found. Using default.");
    }
}

static void save_auth_config(void) {
    nvs_handle_t handle;
    if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) == ESP_OK) {
        nvs_set_u8(handle, NVS_ENABLED_KEY, enabled);
        nvs_commit(handle);
        nvs_close(handle);
        ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled);
    } else {
        ESP_LOGE(TAG, "Failed to save auth config.");
    }
}

static void load_tags_from_nvs(void) {
    nvs_handle_t handle;
    if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
        ESP_LOGW(TAG, "No stored tags in NVS");
        return;
    }

    uint8_t count = 0;
    if (nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count) != ESP_OK) {
        nvs_close(handle);
        return;
    }

    tag_count = 0;

    for (int i = 0; i < count && i < MAX_TAGS; i++) {
        char key[16];
        char tag_buf[AUTH_TAG_MAX_LEN];
        size_t len = sizeof(tag_buf);

        snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
        if (nvs_get_str(handle, key, tag_buf, &len) == ESP_OK) {
            strncpy(valid_tags[tag_count], tag_buf, AUTH_TAG_MAX_LEN - 1);
            valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
            tag_count++;
        }
    }

    nvs_close(handle);
    ESP_LOGI(TAG, "Loaded %d tags from NVS", tag_count);
}

static void save_tags_to_nvs(void) {
    nvs_handle_t handle;
    if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
        ESP_LOGE(TAG, "Failed to open NVS to save tags");
        return;
    }

    nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count);

    for (int i = 0; i < tag_count; i++) {
        char key[16];
        snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i);
        nvs_set_str(handle, key, valid_tags[i]);
    }

    nvs_commit(handle);
    nvs_close(handle);
    ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count);
}

// ===========================
// Internal Helpers
// ===========================

static bool is_tag_valid(const char *tag) {
    for (int i = 0; i < tag_count; i++) {
        if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) {
            return true;
        }
    }
    return false;
}

// ===========================
// Public API
// ===========================


void auth_init(void) {
    load_auth_config();
    load_tags_from_nvs();

    if (enabled) {
        initWiegand();
        ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)");
    } else {
        ESP_LOGI(TAG, "Auth disabled, Wiegand reader not started");
    }

    auth_enabled_event_data_t evt = { .enabled = enabled };
    esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY);
    ESP_LOGI(TAG, "Initial AUTH state sent (enabled = %d)", enabled);
}

void auth_set_enabled(bool value) {
    enabled = value;
    save_auth_config();
    ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED");

    auth_enabled_event_data_t event = { .enabled = enabled };
    esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY);
}

bool auth_is_enabled(void) {
    return enabled;
}

bool auth_add_tag(const char *tag) {
    if (tag_count >= MAX_TAGS) return false;
    if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false;
    if (is_tag_valid(tag)) return true; // Already exists

    strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1);
    valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0';
    tag_count++;

    save_tags_to_nvs();
    ESP_LOGI(TAG, "Tag added: %s", tag);
    return true;
}

bool auth_remove_tag(const char *tag) {
    for (int i = 0; i < tag_count; i++) {
        if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) {
            for (int j = i; j < tag_count - 1; j++) {
                strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN);
            }
            tag_count--;

            save_tags_to_nvs();
            ESP_LOGI(TAG, "Tag removed: %s", tag);
            return true;
        }
    }
    return false;
}

bool auth_tag_exists(const char *tag) {
    return is_tag_valid(tag);
}

void auth_list_tags(void) {
    ESP_LOGI(TAG, "Registered Tags (%d):", tag_count);
    for (int i = 0; i < tag_count; i++) {
        ESP_LOGI(TAG, "- %s", valid_tags[i]);
    }
}

void auth_wait_for_tag_registration(void) {
    waiting_for_registration = true;
    ESP_LOGI(TAG, "Tag registration mode enabled.");
}

void auth_process_tag(const char *tag) {
    if (!tag || !auth_is_enabled()) {
        ESP_LOGW(TAG, "Auth disabled or NULL tag received.");
        return;
    }

    if (waiting_for_registration) {
        if (auth_add_tag(tag)) {
            auth_tag_event_data_t event;
            strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
            event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
            event.authorized = true;

            esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &event, sizeof(event), portMAX_DELAY);
            ESP_LOGI(TAG, "Tag registered: %s", tag);
        } else {
            ESP_LOGW(TAG, "Failed to register tag: %s", tag);
        }
        waiting_for_registration = false;
        return;
    }

    auth_tag_event_data_t event;
    strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1);
    event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0';
    event.authorized = is_tag_valid(tag);

    ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED");

    esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY);
}

int auth_get_tag_count(void) {
    return tag_count;
}

const char *auth_get_tag_by_index(int index) {
    if (index < 0 || index >= tag_count) return NULL;
    return valid_tags[index];
}
