#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_system.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"
#if CONFIG_APP_LINK_BLE
#include "link_ble.h"
#endif
#include "device_model.h"

static const char *tag_s = "WIFI_CFG";
static httpd_handle_t config_httpd_s = NULL;
static volatile bool s_prov_restart_scheduled_s = false;
#if CONFIG_APP_LINK_WIFI
static bool wifi_editable_s = false;
#endif
static bool devid_editable_s = false;

static void provision_restart_task(void *arg)
{
    (void)arg;
    vTaskDelay(pdMS_TO_TICKS(400));
    ESP_LOGI(tag_s, "配网已保存，关闭热点并重启");
#if !CONFIG_APP_LINK_WIFI
    provision_wifi_shutdown();
#endif
    esp_restart();
}

static void provision_schedule_restart(void)
{
    if (s_prov_restart_scheduled_s) {
        return;
    }
    s_prov_restart_scheduled_s = true;
    if (xTaskCreate(provision_restart_task, "prov_restart", 3072, NULL, 10, NULL) != pdPASS) {
        s_prov_restart_scheduled_s = false;
        ESP_LOGE(tag_s, "创建重启任务失败，立即重启");
        provision_wifi_shutdown();
        esp_restart();
    }
}

/**
 * 强制门户：须返回非 204（如 302），Android/iOS 才会弹出「需登录网络」。
 * 旧实现 generate_204 回 204，系统会认为已联网，故不会自动弹网页。
 */
static esp_err_t captive_portal_redirect(httpd_req_t *req)
{
    (void)req;
    httpd_resp_set_status(req, "302 Found");
    httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/");
    httpd_resp_set_hdr(req, "Cache-Control", "no-cache, no-store, must-revalidate");
    httpd_resp_set_hdr(req, "Connection", "close");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

static void register_captive_redirect_uri(httpd_handle_t server, const char *uri)
{
    httpd_uri_t u = {
        .uri = uri,
        .method = HTTP_GET,
        .handler = captive_portal_redirect,
    };
    (void)httpd_register_uri_handler(server, &u);
}

static const char *device_model_to_str(device_model_t model)
{
    switch (model) {
    case DEVICE_MODEL_1102:
        return "1102";
    default:
        return "1101";
    }
}

/** 完整设备号：如 CN110200000001；第 3～6 位须为 1101/1102 */
static bool device_full_id_is_valid(const char *val)
{
    if (val == NULL || val[0] == '\0') {
        return false;
    }
    if (strstr(val, "app2dev/") != NULL || strchr(val, '/') != NULL) {
        return false;
    }

    size_t len = strlen(val);
    if (len < (size_t)(DEVICE_MODEL_SLICE_0BASE_START + DEVICE_MODEL_SLICE_CHAR_LEN) || len >= 32) {
        return false;
    }

    for (size_t i = 0; i < len; i++) {
        if (!isalnum((unsigned char)val[i])) {
            return false;
        }
    }

    const char *slice = val + DEVICE_MODEL_SLICE_0BASE_START;
    if (strncmp(slice, "1101", DEVICE_MODEL_SLICE_CHAR_LEN) == 0 ||
        strncmp(slice, "1102", DEVICE_MODEL_SLICE_CHAR_LEN) == 0) {
        return true;
    }
    return false;
}

static void device_id_save_if_valid(const char *decoded_val)
{
    if (!device_full_id_is_valid(decoded_val)) {
        ESP_LOGW(tag_s, "设备号格式无效（示例 CN110200000001）: %s", decoded_val);
        return;
    }
    save_to_nvs(DEVICE_CFG_KEY_DEVICE_ID, decoded_val);
    ESP_LOGI(tag_s, "设备号已保存: %s → 型号 %s", decoded_val,
              device_model_to_str(device_model_from_full_id(decoded_val)));
}

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

static bool url_decode_safe(char *dst, size_t dst_cap, const char *src)
{
    char a, b;
    size_t out = 0;

    if (dst == NULL || src == NULL || dst_cap == 0) {
        return false;
    }

    while (*src && (out + 1 < dst_cap)) {
        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[out++] = (char)((a << 4) | b);
            src += 3;
        } else if (*src == '+') {
            dst[out++] = ' ';
            src++;
        } else {
            dst[out++] = *src++;
        }
    }
    dst[out] = '\0';

    /* src 未消费完说明被截断，调用方应拒绝本次写入 */
    return (*src == '\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)
{
    char devid_current[32] = {0};
    char devid_status[128];

    read_from_nvs(DEVICE_CFG_KEY_DEVICE_ID, devid_current, sizeof(devid_current));
    sprintf(devid_status, devid_editable_s ? "" : "disabled style='background:#eee'");

#if CONFIG_APP_LINK_BLE
    char ble_status[128];
    sprintf(ble_status, devid_editable_s ? "" : "disabled style='background:#eee'");
    FILE *f = fopen("/spiffs/index_ble.html", "r");
#elif CONFIG_APP_LINK_UART
    FILE *f = fopen("/spiffs/index_uart.html", "r");
#else
    char wifi_status[128];
    sprintf(wifi_status, wifi_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_current, devid_status,
             devid_editable_s ? "🔓" : "🔒", ble_status);
#elif CONFIG_APP_LINK_UART
    snprintf(final_html, fsize + 1024, template,
             devid_editable_s ? "🔓" : "🔒", devid_current, devid_status);
#else
    snprintf(final_html, fsize + 1024, template,
             wifi_editable_s ? "🔓" : "🔒", wifi_status, wifi_status,
             devid_editable_s ? "🔓" : "🔒", devid_current, 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];
                if (!url_decode_safe(decoded_val, sizeof(decoded_val), val)) {
                    ESP_LOGW(tag_s, "表单字段过长或非法编码，已拒绝 key=%s", key);
                    token = strtok_r(NULL, "&", &saveptr);
                    continue;
                }
                if (strcmp(key, "devid") == 0 && devid_editable_s) {
                    device_id_save_if_valid(decoded_val);
                }
#if CONFIG_APP_LINK_BLE
                else if (strcmp(key, "ble_name") == 0 && devid_editable_s) {
                    save_to_nvs(DEVICE_CFG_KEY_BLE_ADV_NAME, decoded_val);
                }
#endif
#if CONFIG_APP_LINK_WIFI
                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);
                }
#endif
            }
            token = strtok_r(NULL, "&", &saveptr);
        }
    }
    provision_schedule_restart();
    httpd_resp_set_status(req, "200 OK");
    httpd_resp_set_hdr(req, "Connection", "close");
    httpd_resp_set_type(req, "text/html; charset=utf-8");
    httpd_resp_send(req, "<h1>Success</h1><p>Restarting...</p>", HTTPD_RESP_USE_STRLEN);
    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 provision_wifi_shutdown(void)
{
#if !CONFIG_APP_LINK_WIFI
    if (config_httpd_s != NULL) {
        (void)httpd_stop(config_httpd_s);
        config_httpd_s = NULL;
    }
    esp_err_t e = esp_wifi_stop();
    if (e != ESP_OK && e != ESP_ERR_WIFI_NOT_INIT) {
        ESP_LOGD(tag_s, "wifi_stop: %s", esp_err_to_name(e));
    }
    e = esp_wifi_deinit();
    if (e != ESP_OK && e != ESP_ERR_WIFI_NOT_INIT) {
        ESP_LOGD(tag_s, "wifi_deinit: %s", esp_err_to_name(e));
    }
#endif
}

void start_config_web(void)
{
#if CONFIG_APP_LINK_BLE
    link_ble_stop();
#endif
    provision_wifi_shutdown();

    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();
    config.uri_match_fn = httpd_uri_match_wildcard;
    config.max_open_sockets = 13;
    config.lru_purge_enable = true;
    if (httpd_start(&server, &config) == ESP_OK) {
        config_httpd_s = server;
        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);
        /* 各系统连网检测 URL（DNS 劫持到本机后命中）→ 302 触发自动弹窗 */
        register_captive_redirect_uri(server, "/generate_204");
        register_captive_redirect_uri(server, "/gen_204");
        register_captive_redirect_uri(server, "/hotspot-detect.html");
        register_captive_redirect_uri(server, "/library/test/success.html");
        register_captive_redirect_uri(server, "/connecttest.txt");
        register_captive_redirect_uri(server, "/redirect");
        register_captive_redirect_uri(server, "/success.txt");
        register_captive_redirect_uri(server, "/canonical.html");
        register_captive_redirect_uri(server, "/favicon.ico");
        /* 微信等任意探测路径 */
        register_captive_redirect_uri(server, "/*");
        httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
        ESP_LOGI(tag_s, "配网门户已启动: http://192.168.4.1 (连网检测→302 弹窗)");
    }
    s_prov_restart_scheduled_s = false;
}

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