#include "wifi_config_agent.h"
#include "wifi_config_config.h"
#include "device_debug_agent.h"
#include "device_diag.h"
#include "device_identity.h"
#include "device_fileopen.h"
#include "device_wifi_manager.h"
#include "common.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static int s_wifi_config_sock = -1;
static pthread_t s_wifi_config_thread;
static volatile int s_wifi_config_running = 0;
static pthread_mutex_t s_wifi_config_sock_mutex = PTHREAD_MUTEX_INITIALIZER;

static int wifi_config_bind_socket(void)
{
    int reuse = 1;
    struct sockaddr_in addr;

    if (s_wifi_config_sock >= 0) {
        close(s_wifi_config_sock);
        s_wifi_config_sock = -1;
    }

    s_wifi_config_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (s_wifi_config_sock < 0) {
        my_zlog_error("WiFi配置助手: 创建 socket 失败: %s", strerror(errno));
        return -1;
    }

    if (setsockopt(s_wifi_config_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        my_zlog_warn("WiFi配置助手: SO_REUSEADDR 设置失败");
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(WIFI_CONFIG_UDP_PORT);

    if (bind(s_wifi_config_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        my_zlog_error("WiFi配置助手: 绑定端口 %d 失败: %s",
                      WIFI_CONFIG_UDP_PORT, strerror(errno));
        close(s_wifi_config_sock);
        s_wifi_config_sock = -1;
        return -1;
    }

    return 0;
}

void wifi_config_agent_rebind_socket(void)
{
    pthread_mutex_lock(&s_wifi_config_sock_mutex);
    if (wifi_config_bind_socket() == 0) {
        my_zlog_info("WiFi配置助手: UDP %d 已重新绑定", WIFI_CONFIG_UDP_PORT);
    }
    pthread_mutex_unlock(&s_wifi_config_sock_mutex);
}

/* 设备诊断 cmd 转发判断（device_check / browser_refresh / browser_reopen / log_fetch） */
static int is_device_diag_cmd(const char *cmd)
{
    return device_diag_is_cmd(cmd);
}

static int is_stream_debug_cmd(const char *cmd)
{
    return cmd && (strcmp(cmd, "stream_mode_query") == 0 ||
                   strcmp(cmd, "stream_url_update") == 0 ||
                   strcmp(cmd, "stream_url_query") == 0);
}

static int is_command_available(const char *cmd)
{
    char check_cmd[256];
    snprintf(check_cmd, sizeof(check_cmd), "which %s > /dev/null 2>&1", cmd);
    return system(check_cmd) == 0;
}

static int ufw_is_active(void)
{
    FILE *fp = popen("ufw status 2>/dev/null", "r");
    char line[256];

    if (!fp)
        return 0;

    while (fgets(line, sizeof(line), fp)) {
        if (strstr(line, "Status: active") != NULL) {
            pclose(fp);
            return 1;
        }
    }

    pclose(fp);
    return 0;
}

static int ufw_has_udp_port_rule(int port)
{
    char cmd[160];
    snprintf(cmd, sizeof(cmd), "ufw status 2>/dev/null | grep -q '%d/udp'", port);
    return system(cmd) == 0;
}

/* ufw 已启用且未放行时自动执行 ufw allow；无 ufw 或未启用则跳过 */
static void ensure_firewall_udp_port(int port)
{
    char allow_cmd[128];
    int ret;

    if (!is_command_available("ufw")) {
        my_zlog_debug("WiFi配置助手: 未安装 ufw，跳过防火墙检查");
        return;
    }

    if (!ufw_is_active()) {
        my_zlog_debug("WiFi配置助手: ufw 未启用，跳过防火墙放行");
        return;
    }

    if (ufw_has_udp_port_rule(port)) {
        my_zlog_debug("WiFi配置助手: UDP %d 已在 ufw 中放行", port);
        return;
    }

    snprintf(allow_cmd, sizeof(allow_cmd), "ufw allow %d/udp >/dev/null 2>&1", port);
    my_zlog_info("WiFi配置助手: UDP %d 未放行，正在执行 ufw allow ...", port);
    ret = system(allow_cmd);
    if (ret == 0 && ufw_has_udp_port_rule(port)) {
        my_zlog_info("WiFi配置助手: UDP %d 放行成功", port);
        return;
    }

    my_zlog_warn("WiFi配置助手: UDP %d 自动放行失败，App 可能无法发现设备", port);
}

typedef struct {
    const char *code;
    const char *name;
} wifi_device_type_entry_t;

static const wifi_device_type_entry_t s_device_type_table[] = {
    {"0101", "大闪电"},
    {"0102", "小闪电"},
    {"0103", "挖掘机"},
    {"0104", "推土机"},
    {"0105", "人坐超大车"},
    {"0106", "F1赛车"},
    {"0107", "大众POLO R"},
    {"0202", "坦克"},
    {"0203", "M1A2坦克"},
    {"0204", "美卡隆坦克"},
    {"0206", "水坦克"},
    {"0207", "射水弹坦克"},
    {"0301", "34号船"},
    {"0302", "ship0302船"},
    {"0401", "云台"},
    {"0403", "定位设备"},
    {"0404", "云台"},
    {"0405", "云台0405"},
    {"0406", "水枪炮台"},
    {"0501", "机器狗"},
    {"0502", "机械臂"},
};

static const char *device_type_name_from_code(const char *type_code)
{
    size_t i;

    if (!type_code || type_code[0] == '\0')
        return "香橙派";

    for (i = 0; i < sizeof(s_device_type_table) / sizeof(s_device_type_table[0]); i++) {
        if (strcmp(type_code, s_device_type_table[i].code) == 0)
            return s_device_type_table[i].name;
    }

    return "香橙派";
}

static void normalize_device_no(char *device_no, size_t size)
{
    size_t len;

    if (!device_no || size == 0)
        return;

    device_no[size - 1] = '\0';
    len = strcspn(device_no, "\r\n");
    device_no[len] = '\0';
}

static const char *get_pure_device_no(void)
{
    const char *device_no = mqtt_topic_pure_number();

    if (device_no && device_no[0] != '\0' && strcmp(device_no, "00000000000000") != 0)
        return device_no;

    device_no = device_id_function();
    if (device_no && device_no[0] != '\0')
        return device_no;

    return NULL;
}

static int parse_device_no_parts(const char *device_no, char *first2, size_t first2_sz,
                                 char *type_code, size_t type_code_sz,
                                 char *last4, size_t last4_sz)
{
    char normalized[WIFI_CONFIG_DEVICE_NO_LEN + 1];

    if (!device_no || !first2 || !type_code || !last4)
        return -1;

    snprintf(normalized, sizeof(normalized), "%s", device_no);
    normalize_device_no(normalized, sizeof(normalized));
    if (strlen(normalized) < WIFI_CONFIG_DEVICE_NO_LEN)
        return -1;

    snprintf(first2, first2_sz, "%.2s", normalized);
    snprintf(type_code, type_code_sz, "%.4s", normalized + 2);
    snprintf(last4, last4_sz, "%.4s", normalized + 10);
    return 0;
}

static void generate_device_id(char *buf, size_t size)
{
    const char *device_no = get_pure_device_no();

    if (!buf || size == 0)
        return;

    if (device_no) {
        snprintf(buf, size, "%s", device_no);
        normalize_device_no(buf, size);
        return;
    }

    snprintf(buf, size, "00000000000000");
}

static void generate_device_name(char *buf, size_t size)
{
    const char *device_no = get_pure_device_no();
    char first2[3];
    char type_code[5];
    char last4[5];
    const char *type_name;

    if (!buf || size == 0)
        return;

    if (!device_no || parse_device_no_parts(device_no, first2, sizeof(first2),
                                            type_code, sizeof(type_code),
                                            last4, sizeof(last4)) != 0) {
        snprintf(buf, size, "香橙派");
        return;
    }

    type_name = device_type_name_from_code(type_code);
    snprintf(buf, size, "%s%s-%s", type_name, first2, last4);
}

static int send_json(int sock, const struct sockaddr_in *addr, const char *json)
{
    ssize_t n;

    if (!json || !addr || sock < 0)
        return -1;

    n = sendto(sock, json, strlen(json), 0, (const struct sockaddr *)addr, sizeof(*addr));
    return n >= 0 ? 0 : -1;
}

static void send_config_result(int sock, const struct sockaddr_in *addr, int success,
                               const char *message)
{
    cJSON *root = cJSON_CreateObject();

    if (!root)
        return;

    cJSON_AddStringToObject(root, "cmd", "config_result");
    cJSON_AddBoolToObject(root, "success", success ? 1 : 0);
    if (message && message[0])
        cJSON_AddStringToObject(root, "message", message);

    char *payload = cJSON_PrintUnformatted(root);
    if (payload) {
        send_json(sock, addr, payload);
        free(payload);
    }
    cJSON_Delete(root);
}

/* config_ack：设备收到 config 后立即回复，表示已收到即将执行 */
static void send_config_ack(int sock, const struct sockaddr_in *addr, int accepted,
                            const char *action, const char *message)
{
    cJSON *root = cJSON_CreateObject();
    char *payload;

    if (!root)
        return;

    cJSON_AddStringToObject(root, "cmd", "config_ack");
    cJSON_AddBoolToObject(root, "accepted", accepted ? 1 : 0);
    if (action && action[0])
        cJSON_AddStringToObject(root, "action", action);
    if (message && message[0])
        cJSON_AddStringToObject(root, "message", message);

    payload = cJSON_PrintUnformatted(root);
    if (payload) {
        send_json(sock, addr, payload);
        free(payload);
    }
    cJSON_Delete(root);
}

static void handle_discover(int sock, const struct sockaddr_in *addr)
{
    char device_id[WIFI_CONFIG_DEVICE_ID_MAX];
    char device_name[WIFI_CONFIG_DEVICE_NAME_MAX];
    cJSON *root;

    generate_device_id(device_id, sizeof(device_id));
    generate_device_name(device_name, sizeof(device_name));
    root = cJSON_CreateObject();
    if (!root)
        return;

    cJSON_AddStringToObject(root, "cmd", "discover_response");
    cJSON_AddStringToObject(root, "deviceId", device_id);
    cJSON_AddStringToObject(root, "name", device_name);

    char *payload = cJSON_PrintUnformatted(root);
    if (payload) {
        send_json(sock, addr, payload);
        free(payload);
    }
    cJSON_Delete(root);
}

static void handle_config(int sock, const struct sockaddr_in *addr, cJSON *root)
{
    cJSON *ssid_item = cJSON_GetObjectItemCaseSensitive(root, "ssid");
    cJSON *password_item = cJSON_GetObjectItemCaseSensitive(root, "password");
    cJSON *action_item = cJSON_GetObjectItemCaseSensitive(root, "action");
    const char *ssid = NULL;
    const char *password = "";
    const char *action = "connect";
    char message[WIFI_CONFIG_MESSAGE_MAX];
    int success;

    /* 参数校验：ssid 非法时仍用旧协议回 config_result，保持兼容 */
    if (!cJSON_IsString(ssid_item) || !ssid_item->valuestring || !ssid_item->valuestring[0]) {
        send_config_result(sock, addr, 0, "缺少有效的 ssid 字段");
        return;
    }

    ssid = ssid_item->valuestring;
    if (cJSON_IsString(password_item) && password_item->valuestring)
        password = password_item->valuestring;
    if (cJSON_IsString(action_item) && action_item->valuestring && action_item->valuestring[0])
        action = action_item->valuestring;

    my_zlog_info("WiFi配置助手: 收到配网请求 SSID=%s action=%s", ssid, action);

    /* 新协议(v3)：先立即回复 config_ack，表示已收到即将执行。
     * 这样 action=connect 切换 WiFi 后，即使手机收不到最终结果，
     * 也已通过 ack 知道设备收到了配置。 */
    snprintf(message, sizeof(message), "%s",
             strcmp(action, "connect") == 0
                 ? "已收到配置，即将切换 WiFi"
                 : "已收到配置，即将保存");
    send_config_ack(sock, addr, 1, action, message);

    if (strcmp(action, "save") == 0) {
        /* save 模式：保存完成后回复 config_result（最终结果） */
        success = orange_pi_save_wifi(ssid, password) == 0;
        snprintf(message, sizeof(message), "%s",
                 success ? "WiFi 配置已保存" : "WiFi 保存失败");
        my_zlog_info("WiFi配置助手: 配网结果 success=%d, message=%s", success, message);
        send_config_result(sock, addr, success, message);
    } else {
        /* connect 模式：只回 config_ack 即可，切 WiFi 后手机收不到最终结果，
         * 不再回复 config_result，避免无意义发送。 */
        success = orange_pi_connect_wifi(ssid, password) == 0;
        my_zlog_info("WiFi配置助手: connect 已执行 success=%d (不回 config_result)", success);
    }
}

static void handle_wifi_list_query(int sock, const struct sockaddr_in *addr)
{
    cJSON *root = cJSON_CreateObject();
    cJSON *list;
    char *payload;

    if (!root)
        return;

    cJSON_AddStringToObject(root, "cmd", "wifi_list_response");
    list = app_wifi_list_query();
    if (!list)
        list = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "list", list);

    payload = cJSON_PrintUnformatted(root);
    if (payload) {
        send_json(sock, addr, payload);
        free(payload);
    }
    cJSON_Delete(root);
    my_zlog_info("WiFi配置助手: 应答 wifi_list_query");
}

static void handle_wifi_switch(int sock, const struct sockaddr_in *addr, cJSON *root)
{
    cJSON *ssid_item = cJSON_GetObjectItemCaseSensitive(root, "ssid");
    cJSON *resp;
    const char *ssid = NULL;
    char message[WIFI_CONFIG_MESSAGE_MAX];
    int success;
    int rc;
    char *payload;

    if (!cJSON_IsString(ssid_item) || !ssid_item->valuestring || !ssid_item->valuestring[0]) {
        resp = cJSON_CreateObject();
        cJSON_AddStringToObject(resp, "cmd", "wifi_switch_result");
        cJSON_AddBoolToObject(resp, "success", 0);
        cJSON_AddStringToObject(resp, "message", "缺少有效的 ssid 字段");
        payload = cJSON_PrintUnformatted(resp);
        if (payload) { send_json(sock, addr, payload); free(payload); }
        cJSON_Delete(resp);
        return;
    }

    ssid = ssid_item->valuestring;
    my_zlog_info("WiFi配置助手: 收到 wifi_switch SSID=%s", ssid);

    /* 先校验 SSID 是否已保存（不连接），据此先回复 app，
     * 再实际发起切换。避免切到新 WiFi 后 app 收不到响应。 */
    rc = app_wifi_check_saved(ssid);
    if (rc != 0) {
        /* 未保存：直接回失败 */
        resp = cJSON_CreateObject();
        cJSON_AddStringToObject(resp, "cmd", "wifi_switch_result");
        cJSON_AddBoolToObject(resp, "success", 0);
        snprintf(message, sizeof(message), "未找到已保存的 WiFi：%s", ssid);
        cJSON_AddStringToObject(resp, "message", message);
        payload = cJSON_PrintUnformatted(resp);
        if (payload) { send_json(sock, addr, payload); free(payload); }
        cJSON_Delete(resp);
        return;
    }

    /* 已保存：立即回成功，告知 app 已发起切换 */
    resp = cJSON_CreateObject();
    cJSON_AddStringToObject(resp, "cmd", "wifi_switch_result");
    cJSON_AddBoolToObject(resp, "success", 1);
    snprintf(message, sizeof(message), "WiFi 切换成功，正在连接 %s", ssid);
    cJSON_AddStringToObject(resp, "message", message);
    payload = cJSON_PrintUnformatted(resp);
    if (payload) { send_json(sock, addr, payload); free(payload); }
    cJSON_Delete(resp);

    /* 回复完毕，再实际发起切换（切换结果不再回，切网后 app 收不到） */
    rc = app_wifi_switch(ssid);
    if (rc != 0)
        my_zlog_error("WiFi配置助手: wifi_switch 实际切换失败 SSID=%s rc=%d", ssid, rc);
    else
        my_zlog_info("WiFi配置助手: wifi_switch 已发起切换 SSID=%s", ssid);
}

static void handle_wifi_delete(int sock, const struct sockaddr_in *addr, cJSON *root)
{
    cJSON *ssid_item = cJSON_GetObjectItemCaseSensitive(root, "ssid");
    cJSON *resp;
    const char *ssid = NULL;
    char message[WIFI_CONFIG_MESSAGE_MAX];
    int success;
    int rc;
    char *payload;

    if (!cJSON_IsString(ssid_item) || !ssid_item->valuestring || !ssid_item->valuestring[0]) {
        resp = cJSON_CreateObject();
        cJSON_AddStringToObject(resp, "cmd", "wifi_delete_result");
        cJSON_AddBoolToObject(resp, "success", 0);
        cJSON_AddStringToObject(resp, "message", "缺少有效的 ssid 字段");
        payload = cJSON_PrintUnformatted(resp);
        if (payload) { send_json(sock, addr, payload); free(payload); }
        cJSON_Delete(resp);
        return;
    }

    ssid = ssid_item->valuestring;
    my_zlog_info("WiFi配置助手: 收到 wifi_delete SSID=%s", ssid);

    rc = app_wifi_delete(ssid);
    success = (rc == 0);
    if (rc == 0)
        snprintf(message, sizeof(message), "已删除 WiFi：%s", ssid);
    else if (rc == -2)
        snprintf(message, sizeof(message), "受保护的 WiFi 不可删除：%s", ssid);
    else
        snprintf(message, sizeof(message), "未找到或正在使用的 WiFi：%s", ssid);

    resp = cJSON_CreateObject();
    cJSON_AddStringToObject(resp, "cmd", "wifi_delete_result");
    cJSON_AddBoolToObject(resp, "success", success ? 1 : 0);
    cJSON_AddStringToObject(resp, "message", message);

    payload = cJSON_PrintUnformatted(resp);
    if (payload) { send_json(sock, addr, payload); free(payload); }
    cJSON_Delete(resp);
}

static void process_message(int sock, const char *buf, const struct sockaddr_in *addr)
{
    cJSON *root = cJSON_Parse(buf);
    cJSON *cmd_item;

    if (!root) {
        my_zlog_warn("WiFi配置助手: JSON 解析失败");
        return;
    }

    cmd_item = cJSON_GetObjectItemCaseSensitive(root, "cmd");
    if (!cJSON_IsString(cmd_item) || !cmd_item->valuestring) {
        cJSON_Delete(root);
        return;
    }

    if (is_stream_debug_cmd(cmd_item->valuestring)) {
        wifi_config_agent_rebind_socket();
    }

    if (strcmp(cmd_item->valuestring, "discover") == 0) {
        handle_discover(sock, addr);
    } else if (strcmp(cmd_item->valuestring, "config") == 0) {
        handle_config(sock, addr, root);
    } else if (strcmp(cmd_item->valuestring, "stream_mode_query") == 0) {
        stream_debug_handle_mode_query(sock, addr);
    } else if (strcmp(cmd_item->valuestring, "stream_url_update") == 0) {
        stream_debug_handle_url_update(sock, addr, root);
    } else if (strcmp(cmd_item->valuestring, "stream_url_query") == 0) {
        stream_debug_handle_url_query(sock, addr);
    } else if (strcmp(cmd_item->valuestring, "wifi_list_query") == 0) {
        handle_wifi_list_query(sock, addr);
    } else if (strcmp(cmd_item->valuestring, "wifi_switch") == 0) {
        handle_wifi_switch(sock, addr, root);
    } else if (strcmp(cmd_item->valuestring, "wifi_delete") == 0) {
        handle_wifi_delete(sock, addr, root);
    } else if (strcmp(cmd_item->valuestring, "device_check") == 0) {
        device_diag_handle_check(sock, addr);
    } else if (strcmp(cmd_item->valuestring, "browser_refresh") == 0) {
        device_diag_handle_browser_ctrl(sock, addr, "refresh");
    } else if (strcmp(cmd_item->valuestring, "browser_reopen") == 0) {
        device_diag_handle_browser_ctrl(sock, addr, "reopen");
    } else if (strcmp(cmd_item->valuestring, "log_fetch") == 0) {
        device_diag_handle_log_fetch(sock, addr, root);
    }

    cJSON_Delete(root);
}

static void *wifi_config_agent_thread(void *arg)
{
    char buf[WIFI_CONFIG_RECV_BUF_SIZE];
    char device_id[WIFI_CONFIG_DEVICE_ID_MAX];
    char device_name[WIFI_CONFIG_DEVICE_NAME_MAX];
    char first2[3];
    char type_code[5];
    char last4[5];
    const char *type_name;
    (void)arg;

    generate_device_id(device_id, sizeof(device_id));
    generate_device_name(device_name, sizeof(device_name));
    if (parse_device_no_parts(get_pure_device_no(), first2, sizeof(first2),
                              type_code, sizeof(type_code), last4, sizeof(last4)) == 0) {
        type_name = device_type_name_from_code(type_code);
        my_zlog_info("WiFi配置助手对接服务已启动 (UDP)");
        my_zlog_info("  设备号:   %s", device_id);
        my_zlog_info("  类型:     %s (编码 %s)", type_name, type_code);
        my_zlog_info("  短码:     %s%s", first2, last4);
        my_zlog_info("  显示名称: %s", device_name);
    } else {
        my_zlog_info("WiFi配置助手对接服务已启动 (UDP)");
        my_zlog_info("  设备号:   %s", device_id);
        my_zlog_info("  显示名称: %s", device_name);
    }
    my_zlog_info("  监听:     0.0.0.0:%d/udp", WIFI_CONFIG_UDP_PORT);
    my_zlog_info("等待 App 连接...");

    while (s_wifi_config_running) {
        struct sockaddr_in client;
        socklen_t client_len = sizeof(client);
        ssize_t n = recvfrom(s_wifi_config_sock, buf, sizeof(buf) - 1, 0,
                             (struct sockaddr *)&client, &client_len);

        if (n < 0) {
            if (!s_wifi_config_running)
                break;
            if (errno == EINTR)
                continue;
            my_zlog_error("WiFi配置助手: recvfrom 失败: %s", strerror(errno));
            continue;
        }

        buf[n] = '\0';
        process_message(s_wifi_config_sock, buf, &client);
    }

    return NULL;
}

int wifi_config_agent_start(void)
{
    if (s_wifi_config_running)
        return 0;

    ensure_firewall_udp_port(WIFI_CONFIG_UDP_PORT);

    pthread_mutex_lock(&s_wifi_config_sock_mutex);
    if (wifi_config_bind_socket() != 0) {
        pthread_mutex_unlock(&s_wifi_config_sock_mutex);
        return -1;
    }
    pthread_mutex_unlock(&s_wifi_config_sock_mutex);

    s_wifi_config_running = 1;
    if (pthread_create(&s_wifi_config_thread, NULL, wifi_config_agent_thread, NULL) != 0) {
        my_zlog_error("WiFi配置助手: 创建线程失败");
        s_wifi_config_running = 0;
        pthread_mutex_lock(&s_wifi_config_sock_mutex);
        close(s_wifi_config_sock);
        s_wifi_config_sock = -1;
        pthread_mutex_unlock(&s_wifi_config_sock_mutex);
        return -1;
    }

    return 0;
}

void wifi_config_agent_stop(void)
{
    if (!s_wifi_config_running)
        return;

    s_wifi_config_running = 0;
    if (s_wifi_config_sock >= 0) {
        shutdown(s_wifi_config_sock, SHUT_RDWR);
        close(s_wifi_config_sock);
        s_wifi_config_sock = -1;
    }
    pthread_join(s_wifi_config_thread, NULL);
}
