// =========================
// auth_api.c (nova interface por modo)
// =========================

#include "auth_api.h"
#include "auth.h"
#include "esp_log.h"
#include "cJSON.h"
#include <string.h>

static const char *TAG = "auth_api";

// =================================
// Dummy user storage (static list)
// =================================

static struct {
    char username[128];
} users[10] = { {"admin"}, {"user1"} };

static int num_users = 2;

// =================================
// Helpers
// =================================

static void send_json(httpd_req_t *req, cJSON *json) {
    char *str = cJSON_PrintUnformatted(json);
    httpd_resp_set_type(req, "application/json");
    httpd_resp_sendstr(req, str ? str : "{}");
    if (str) free(str);
    cJSON_Delete(json);
}

static esp_err_t recv_body(httpd_req_t *req, char *buf, size_t buf_sz, int *out_len) {
    int remaining = req->content_len;
    int received = 0;

    if (remaining <= 0) {
        *out_len = 0;
        return ESP_OK;
    }

    while (remaining > 0 && received < (int)(buf_sz - 1)) {
        int chunk = remaining;
        int space = (int)(buf_sz - 1 - received);
        if (chunk > space) chunk = space;

        int ret = httpd_req_recv(req, buf + received, chunk);
        if (ret <= 0) return ESP_FAIL;

        received += ret;
        remaining -= ret;
    }
    buf[received] = '\0';
    *out_len = received;
    return ESP_OK;
}


static void trim_in_place(char *s) {
    if (!s || s[0] == '\0') return;

    char *start = s;
    while (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n') {
        start++;
    }

    if (start != s) {
        memmove(s, start, strlen(start) + 1);
    }

    size_t len = strlen(s);
    while (len > 0) {
        char c = s[len - 1];
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
            s[len - 1] = '\0';
            len--;
        } else {
            break;
        }
    }
}

static void strip_wrapping_quotes_in_place(char *s) {
    size_t len;
    if (!s) return;

    trim_in_place(s);
    len = strlen(s);
    if (len >= 2 && s[0] == '"' && s[len - 1] == '"') {
        memmove(s, s + 1, len - 2);
        s[len - 2] = '\0';
        trim_in_place(s);
    }
}

static bool extract_username_from_body(const char *buf, int len, char *out, size_t out_sz) {
    bool ok = false;
    cJSON *json = cJSON_ParseWithLength(buf, len);
    out[0] = '\0';

    if (json) {
        if (cJSON_IsString(json) && json->valuestring) {
            strlcpy(out, json->valuestring, out_sz);
            ok = true;
        } else if (cJSON_IsObject(json)) {
            cJSON *username_js = cJSON_GetObjectItem(json, "username");
            if (cJSON_IsString(username_js) && username_js->valuestring) {
                strlcpy(out, username_js->valuestring, out_sz);
                ok = true;
            }
        }
        cJSON_Delete(json);
    }

    if (!ok) {
        strlcpy(out, buf, out_sz);
    }

    trim_in_place(out);
    strip_wrapping_quotes_in_place(out);
    return out[0] != '\0';
}

static bool usernames_equal_normalized(const char *a, const char *b) {
    char na[128] = {0};
    char nb[128] = {0};

    strlcpy(na, a ? a : "", sizeof(na));
    strlcpy(nb, b ? b : "", sizeof(nb));
    trim_in_place(na);
    trim_in_place(nb);
    strip_wrapping_quotes_in_place(na);
    strip_wrapping_quotes_in_place(nb);
    return strcmp(na, nb) == 0;
}

static int find_user_index(const char *username) {
    for (int i = 0; i < num_users; ++i) {
        if (usernames_equal_normalized(users[i].username, username)) {
            return i;
        }
    }
    return -1;
}

// =================================
// Auth Mode (NEW API)
// =================================

static esp_err_t auth_mode_get_handler(httpd_req_t *req) {
    auth_mode_t mode = auth_get_mode();
    cJSON *json = cJSON_CreateObject();
    cJSON_AddStringToObject(json, "mode", auth_mode_to_str(mode));
    send_json(req, json);
    return ESP_OK;
}

static esp_err_t auth_mode_post_handler(httpd_req_t *req) {
    char buf[256];
    int len = 0;
    if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK) {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados");
        return ESP_FAIL;
    }

    cJSON *json = cJSON_ParseWithLength(buf, len);
    if (!json) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido");
        return ESP_FAIL;
    }

    cJSON *mode_js = cJSON_GetObjectItem(json, "mode");
    if (!cJSON_IsString(mode_js) || mode_js->valuestring == NULL) {
        cJSON_Delete(json);
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'mode' inválido ou ausente");
        return ESP_FAIL;
    }

    auth_mode_t mode;
    if (!auth_mode_from_str(mode_js->valuestring, &mode)) {
        cJSON_Delete(json);
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Valor de 'mode' inválido (use: open|local|ocpp)");
        return ESP_FAIL;
    }

    auth_set_mode(mode);
    cJSON_Delete(json);

    cJSON *resp = cJSON_CreateObject();
    cJSON_AddStringToObject(resp, "mode", auth_mode_to_str(mode));
    send_json(req, resp);
    return ESP_OK;
}

// =================================
/* Users (mock) */
// =================================

static esp_err_t users_get_handler(httpd_req_t *req) {
    cJSON *root = cJSON_CreateObject();
    cJSON *list = cJSON_CreateArray();
    for (int i = 0; i < num_users; ++i) {
        cJSON *u = cJSON_CreateObject();
        cJSON_AddStringToObject(u, "username", users[i].username);
        cJSON_AddItemToArray(list, u);
    }
    cJSON_AddItemToObject(root, "users", list);
    send_json(req, root);
    return ESP_OK;
}

static esp_err_t users_post_handler(httpd_req_t *req) {
    char buf[128];
    char username[128] = {0};
    int len = 0;

    if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK || len <= 0) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Body vazio");
        return ESP_FAIL;
    }

    if (!extract_username_from_body(buf, len, username, sizeof(username))) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'username' inválido ou ausente");
        return ESP_FAIL;
    }

    if (find_user_index(username) >= 0) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário já existe");
        return ESP_FAIL;
    }

    if (num_users >= 10) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido");
        return ESP_FAIL;
    }

    strlcpy(users[num_users].username, username, sizeof(users[num_users].username));
    num_users++;
    httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
    return ESP_OK;
}

static esp_err_t users_delete_handler(httpd_req_t *req) {
    char query[128];
    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
        char username[128] = {0};
        if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) {
            int idx = find_user_index(username);
            if (idx >= 0) {
                for (int j = idx; j < num_users - 1; j++) {
                    users[j] = users[j + 1];
                }
                num_users--;
                httpd_resp_sendstr(req, "Usuário removido com sucesso");
                return ESP_OK;
            }
        }
    }
    httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado");
    return ESP_FAIL;
}

// =================================
// Tags (apenas úteis no modo LOCAL)
// =================================

static esp_err_t tags_get_handler(httpd_req_t *req) {
    cJSON *root = cJSON_CreateObject();
    cJSON *list = cJSON_CreateArray();

    int count = auth_get_tag_count();
    for (int i = 0; i < count; i++) {
        const char *tag = auth_get_tag_by_index(i);
        if (tag) cJSON_AddItemToArray(list, cJSON_CreateString(tag));
    }

    cJSON_AddItemToObject(root, "tags", list);
    send_json(req, root);
    return ESP_OK;
}

static esp_err_t tags_delete_handler(httpd_req_t *req) {
    char query[128];
    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
        char tag[AUTH_TAG_MAX_LEN];
        if (httpd_query_key_value(query, "tag", tag, sizeof(tag)) == ESP_OK) {
            if (auth_remove_tag(tag)) {
                httpd_resp_sendstr(req, "Tag removida com sucesso");
                return ESP_OK;
            }
        }
    }
    httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Tag não encontrada ou inválida");
    return ESP_FAIL;
}

static esp_err_t tags_register_handler(httpd_req_t *req) {
    if (auth_get_mode() != AUTH_MODE_LOCAL_RFID) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Registo de tags disponível apenas no modo LOCAL");
        return ESP_FAIL;
    }
    auth_wait_for_tag_registration();
    httpd_resp_sendstr(req, "Modo de registro de tag ativado");
    return ESP_OK;
}

// =================================
// Register All REST Endpoints
// =================================

void register_auth_handlers(httpd_handle_t server, void *ctx) {
    // Auth mode
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/auth-mode",
        .method = HTTP_GET,
        .handler = auth_mode_get_handler,
        .user_ctx = ctx
    });
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/auth-mode",
        .method = HTTP_POST,
        .handler = auth_mode_post_handler,
        .user_ctx = ctx
    });

    // Users (mock)
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/users",
        .method = HTTP_GET,
        .handler = users_get_handler,
        .user_ctx = ctx
    });
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/users",
        .method = HTTP_POST,
        .handler = users_post_handler,
        .user_ctx = ctx
    });
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/users",
        .method = HTTP_DELETE,
        .handler = users_delete_handler,
        .user_ctx = ctx
    });

    // Tags
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/tags",
        .method = HTTP_GET,
        .handler = tags_get_handler,
        .user_ctx = ctx
    });
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/tags",
        .method = HTTP_DELETE,
        .handler = tags_delete_handler,
        .user_ctx = ctx
    });
    httpd_register_uri_handler(server, &(httpd_uri_t){
        .uri = "/api/v1/config/tags/register",
        .method = HTTP_POST,
        .handler = tags_register_handler,
        .user_ctx = ctx
    });
}
