#include "wifidevnum_config.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_http_server.h"
#include "driver/gpio.h"
#include "lwip/sockets.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_spiffs.h"
#include "sdkconfig.h"
#if CONFIG_APP_LINK_WIFI
#include "mqttconf_commun.h"
#endif
#include "task_manager.h"

static const char *tag_s = "WIFI_CFG";
#if CONFIG_APP_LINK_WIFI
static bool wifi_editable_s = false;
#endif
static bool devid_editable_s = false;

esp_err_t init_spiffs(void)
{
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = "storage",
        .max_files = 5,
        .format_if_mount_failed = true
    };
    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK) {
        ESP_LOGE(tag_s, "SPIFFS 挂载失败: %s", esp_err_to_name(ret));
        return ret;
    }
    return ESP_OK;
}

void url_decode(char *dst, const char *src)
{
    char a, b;
    while (*src) {
        if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) {
            a = toupper((unsigned char)a);
            b = toupper((unsigned char)b);
            a -= (a >= 'A') ? ('A' - 10) : '0';
            b -= (b >= 'A') ? ('A' - 10) : '0';
            *dst++ = (char)((a << 4) | b);
            src += 3;
        } else if (*src == '+') {
            *dst++ = ' ';
            src++;
        } else {
            *dst++ = *src++;
        }
    }
    *dst = '\0';
}

void save_to_nvs(const char *key, const char *value)
{
    nvs_handle_t handle;
    if (nvs_open(DEVICE_CFG_NVS_NAMESPACE, NVS_READWRITE, &handle) == ESP_OK) {
        nvs_set_str(handle, key, value);
        nvs_commit(handle);
        nvs_close(handle);
        ESP_LOGI(tag_s, "NVS 写入成功 [%s]", key);
    }
}

esp_err_t read_from_nvs(const char *key, char *buf, size_t len)
{
    nvs_handle_t handle;
    esp_err_t err = nvs_open(DEVICE_CFG_NVS_NAMESPACE, NVS_READONLY, &handle);
    if (err == ESP_OK) {
        err = nvs_get_str(handle, key, buf, &len);
        nvs_close(handle);
    }
    return err;
}

static esp_err_t get_handler(httpd_req_t *req)
{
#if CONFIG_APP_LINK_BLE
    char devid_status[128], ble_status[128];
    sprintf(devid_status, devid_editable_s ? "" : "disabled style='background:#eee'");
    sprintf(ble_status, devid_editable_s ? "" : "disabled style='background:#eee'");
    FILE *f = fopen("/spiffs/index_ble.html", "r");
#elif CONFIG_APP_LINK_UART
    char devid_status[128];
    sprintf(devid_status, devid_editable_s ? "" : "disabled style='background:#eee'");
    FILE *f = fopen("/spiffs/index_uart.html", "r");
#else
    char wifi_status[128], devid_status[128];
    sprintf(wifi_status, wifi_editable_s ? "" : "disabled style='background:#eee'");
    sprintf(devid_status, devid_editable_s ? "" : "disabled style='background:#eee'");
    FILE *f = fopen("/spiffs/index.html", "r");
#endif
    if (!f) {
        httpd_resp_send_404(req);
        return ESP_FAIL;
    }
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    char *template = malloc(fsize + 1);
    fread(template, 1, fsize, f);
    fclose(f);
    template[fsize] = 0;

    char *final_html = malloc(fsize + 1024);
#if CONFIG_APP_LINK_BLE
    snprintf(final_html, fsize + 1024, template,
             devid_editable_s ? "🔓" : "🔒", devid_status,
             devid_editable_s ? "🔓" : "🔒", ble_status);
#elif CONFIG_APP_LINK_UART
    snprintf(final_html, fsize + 1024, template, devid_editable_s ? "🔓" : "🔒", devid_status);
#else
    snprintf(final_html, fsize + 1024, template,
             wifi_editable_s ? "🔓" : "🔒", wifi_status, wifi_status,
             devid_editable_s ? "🔓" : "🔒", devid_status);
#endif

    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, final_html, HTTPD_RESP_USE_STRLEN);

    free(template);
    free(final_html);
    return ESP_OK;
}

static esp_err_t post_handler(httpd_req_t *req)
{
    char buf[512];
    int cl = req->content_len;
    if (cl < 0) {
        cl = 0;
    }
    size_t max_recv = sizeof(buf) - 1;
    if ((size_t)cl < max_recv) {
        max_recv = (size_t)cl;
    }
    int ret = httpd_req_recv(req, buf, max_recv);
    if (ret > 0) {
        buf[ret] = '\0';
        char *token, *saveptr;
        token = strtok_r(buf, "&", &saveptr);
        while (token != NULL) {
            char *key = token;
            char *val = strchr(token, '=');
            if (val) {
                *val = '\0';
                val++;
                char decoded_val[64];
                url_decode(decoded_val, val);
#if CONFIG_APP_LINK_BLE
                if (strcmp(key, "devid") == 0 && devid_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_DEVICE_ID, decoded_val);
                } else if (strcmp(key, "ble_name") == 0 && devid_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_BLE_ADV_NAME, decoded_val);
                }
#elif CONFIG_APP_LINK_UART
                if (strcmp(key, "devid") == 0 && devid_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_DEVICE_ID, decoded_val);
                }
#else
                if (strcmp(key, "ssid") == 0 && wifi_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_WIFI_SSID, decoded_val);
                } else if (strcmp(key, "pass") == 0 && wifi_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_WIFI_PASS, decoded_val);
                } else if (strcmp(key, "devid") == 0 && devid_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_DEVICE_ID, decoded_val);
                }
#endif
            }
            token = strtok_r(NULL, "&", &saveptr);
        }
    }
    httpd_resp_send(req, "<h1>Success</h1><p>Restarting...</p>", HTTPD_RESP_USE_STRLEN);
    vTaskDelay(pdMS_TO_TICKS(1500));
    esp_restart();
    return ESP_OK;
}

void dns_server_task(void *pvParameters)
{
    enum { DNS_RX_BUF_SIZE = 256 };
    static const uint8_t dns_answer[] = {
        0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x04, 192, 168, 4, 1};
    const size_t dns_answer_len = sizeof(dns_answer);

    uint8_t rx_buffer[DNS_RX_BUF_SIZE];
    struct sockaddr_in dest_addr;
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(53);
    bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    while (1) {
        struct sockaddr_in source_addr;
        socklen_t socklen = sizeof(source_addr);
        /* 预留应答尾部长度，避免 memcpy(rx_buffer + len, answer, ...) 越界 */
        int len = recvfrom(sock, rx_buffer, DNS_RX_BUF_SIZE - dns_answer_len, 0,
                           (struct sockaddr *)&source_addr, &socklen);
        if (len > 12 && (size_t)len + dns_answer_len <= DNS_RX_BUF_SIZE) {
            rx_buffer[2] |= 0x80;
            rx_buffer[3] |= 0x80;
            rx_buffer[7] = 1;
            memcpy(rx_buffer + len, dns_answer, dns_answer_len);
            sendto(sock, rx_buffer, len + dns_answer_len, 0, (struct sockaddr *)&source_addr,
                   sizeof(source_addr));
        }
    }
}

esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{
    (void)err;
    httpd_resp_set_status(req, "302 Found");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

void start_config_web(void)
{
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_err_t e = esp_wifi_init(&cfg);
    if (e != ESP_OK && e != ESP_ERR_INVALID_STATE) {
        ESP_LOGE(tag_s, "wifi_init 失败: %s", esp_err_to_name(e));
        return;
    }
    (void)esp_wifi_stop();
    esp_netif_create_default_wifi_ap();
    wifi_config_t ap_cfg = {
        .ap = {.ssid = CONFIG_ROBIOT_WIFI_SSID, .max_connection = 4, .authmode = WIFI_AUTH_OPEN}
    };
    esp_wifi_set_mode(WIFI_MODE_AP);
    esp_wifi_set_config(WIFI_IF_AP, &ap_cfg);
    esp_wifi_start();
    (void)app_task_start(APP_TASK_DNS_SERVER, dns_server_task, NULL, NULL);
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    if (httpd_start(&server, &config) == ESP_OK) {
        httpd_uri_t get_uri = {.uri = "/", .method = HTTP_GET, .handler = get_handler};
        httpd_register_uri_handler(server, &get_uri);
        httpd_uri_t post_uri = {.uri = "/save", .method = HTTP_POST, .handler = post_handler};
        httpd_register_uri_handler(server, &post_uri);
        httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
    }
}

void nowifidata_start_config_web(void)
{
#if CONFIG_APP_LINK_WIFI
    wifi_editable_s = true;
    devid_editable_s = true;
#else
    devid_editable_s = true;
#endif
    start_config_web();
}

void button_monitor_task(void *arg)
{
    (void)arg;
    gpio_config_t io_conf = {
#if CONFIG_APP_LINK_UART
        /* UART 模式：仅使用设备号重置按键 (GPIO4) */
        .pin_bit_mask = (1ULL << BTN_DEVID_IO),
#else
        /* WiFi/BLE 模式：使用两个按键 */
        .pin_bit_mask = (1ULL << BTN_WIFI_IO) | (1ULL << BTN_DEVID_IO),
#endif
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = 1
    };
    gpio_config(&io_conf);
#if CONFIG_APP_LINK_UART
    int dev_cnt = 0;
#else
    int wifi_cnt = 0, dev_cnt = 0;
#endif
    bool web_started = false;
    while (1) {
#if !CONFIG_APP_LINK_UART
        /* WiFi/BLE 模式：处理 WiFi 配置按键 (GPIO0) */
        if (gpio_get_level(BTN_WIFI_IO) == 0) {
#if CONFIG_APP_LINK_BLE
            if (!devid_editable_s) {
            }

            if (++wifi_cnt == 20) {
                devid_editable_s = true;
                if (!web_started) {
                    start_config_web();
                    web_started = true;
                }
            }
#else
            if (!wifi_editable_s) {
            }

            if (++wifi_cnt == 20) {
                wifi_editable_s = true;
                if (!web_started) {
                    mqtt_manager_stop();
                    start_config_web();
                    web_started = true;
                }
            }
#endif
        } else {
            wifi_cnt = 0;
        }
#endif /* !CONFIG_APP_LINK_UART */

        /* 所有模式：处理设备号重置按键 (GPIO4) */
        if (gpio_get_level(BTN_DEVID_IO) == 0) {
            if (!devid_editable_s) {
            }

            if (++dev_cnt == 20) {
                devid_editable_s = true;
                if (!web_started) {
#if CONFIG_APP_LINK_WIFI
                    mqtt_manager_stop();
#endif
#if CONFIG_APP_LINK_UART
                    /* UART 模式：停止 UART 通信，进入配网模式 */
                    extern void link_uart_stop(void);
                    link_uart_stop();
#endif
                    start_config_web();
                    web_started = true;
                }
            }
        } else {
            dev_cnt = 0;
        }

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void button_monitor_task_init(void)
{
    (void)app_task_start(APP_TASK_BUTTON_MONITOR, button_monitor_task, NULL, NULL);
}
