#include "sdkconfig.h"
#include "remote_control.h"
#if CONFIG_APP_LINK_UART
#include "link_uart.h"
#endif
#include "rc_pwm_control.h"
#include "task_manager.h"
#include "ota_binary_stream.h"
#include "ota_offer_protocol.h"
#include "cJSON.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static const char *tag_s = "REMOTE_CTRL";

#define RC_WATCHDOG_PERIOD_MS 100

static volatile uint32_t s_last_cmd_time_ms = 0;
static volatile bool s_watchdog_running = false;
static volatile bool s_timeout_stopped = false;
static TaskHandle_t s_watchdog_handle = NULL;
static TaskHandle_t s_stop_notify_handle = NULL;
static volatile bool s_stop_notify_active = false;
static remote_control_stop_notify_cb_t s_stop_notify_cb = NULL;

static uint32_t get_current_time_ms(void)
{
    return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS);
}

char *remote_control_stop_json_malloc(void)
{
    cJSON *body = cJSON_CreateObject();
    cJSON *pwm = cJSON_CreateObject();
    if (!body || !pwm) {
        cJSON_Delete(body);
        cJSON_Delete(pwm);
        return NULL;
    }
    cJSON_AddNumberToObject(pwm, "speed", 0);
    cJSON_AddNumberToObject(pwm, "direction", 0);
    cJSON_AddNumberToObject(pwm, "mode", 0);
    cJSON_AddNumberToObject(pwm, "val", 0);
    cJSON_AddItemToObject(body, "pwm_ctrl", pwm);

    cJSON *root = cJSON_CreateObject();
    cJSON *head = cJSON_CreateObject();
    if (!root || !head) {
        cJSON_Delete(root);
        cJSON_Delete(head);
        cJSON_Delete(body);
        return NULL;
    }
    cJSON_AddNumberToObject(head, "message_type", 3);
    cJSON_AddItemToObject(root, "head", head);
    cJSON_AddItemToObject(root, "body", body);
    char *out = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    return out;
}

void remote_control_set_stop_notify_cb(remote_control_stop_notify_cb_t cb)
{
    s_stop_notify_cb = cb;
}

static void remote_control_stop_notify_push_once(void)
{
    if (s_stop_notify_cb == NULL) {
        return;
    }
    char *json = remote_control_stop_json_malloc();
    if (json == NULL) {
        return;
    }
    s_stop_notify_cb(json);
    free(json);
}

static void remote_control_stop_notify_task(void *param)
{
    (void)param;
    while (s_stop_notify_active) {
        if (s_timeout_stopped) {
            remote_control_stop_notify_push_once();
        }
        vTaskDelay(pdMS_TO_TICKS(RC_STOP_NOTIFY_PERIOD_MS));
    }
    s_stop_notify_handle = NULL;
    vTaskDelete(NULL);
}

static void remote_control_stop_notify_begin(void)
{
    if (s_stop_notify_handle != NULL) {
        return;
    }
#if CONFIG_APP_LINK_UART
    if (!link_uart_has_peer_seen()) {
        return;
    }
#endif
    s_stop_notify_active = true;
    /* 禁止在 rc_wdog 小栈任务里同步 cJSON+BLE Notify；由 rc_stop_ntf 任务发送 */
    if (xTaskCreate(remote_control_stop_notify_task, "rc_stop_ntf", 4096, NULL, 5,
                    &s_stop_notify_handle) != pdPASS) {
        s_stop_notify_active = false;
        s_stop_notify_handle = NULL;
        ESP_LOGW(tag_s, "停车 Notify 任务创建失败");
    }
}

void remote_control_stop_notify_end(void)
{
    s_stop_notify_active = false;
}

static bool remote_control_is_stop_pwm_json(cJSON *pwm_ctrl)
{
    if (!pwm_ctrl || !cJSON_IsObject(pwm_ctrl)) {
        return false;
    }
    cJSON *speed = cJSON_GetObjectItem(pwm_ctrl, "speed");
    cJSON *direction = cJSON_GetObjectItem(pwm_ctrl, "direction");
    if (!speed || !direction || !cJSON_IsNumber(speed) || !cJSON_IsNumber(direction)) {
        return false;
    }
    return (speed->valueint == 0 && direction->valueint == 0);
}

static void rc_watchdog_task(void *param)
{
    (void)param;
    s_watchdog_running = true;
    
    while (s_watchdog_running) {
        vTaskDelay(pdMS_TO_TICKS(RC_WATCHDOG_PERIOD_MS));
        
        uint32_t now = get_current_time_ms();
        uint32_t last = s_last_cmd_time_ms;
        
        /* 已初始化且超过 500ms 未收到命令：仅首次进入超时态时停车 */
        if (last != 0 && (now - last) > RC_CMD_TIMEOUT_MS) {
            if (!s_timeout_stopped) {
                rc_vehicle_stop();
                s_timeout_stopped = true;
                remote_control_stop_notify_begin();
                ESP_LOGI(tag_s, "RC timeout: no cmd for %d ms, vehicle stopped",
                         (int)(now - last));
            }
        }
    }
    
    s_watchdog_handle = NULL;
    vTaskDelete(NULL);
}

esp_err_t remote_control_watchdog_start(void)
{
    if (s_watchdog_handle != NULL) {
        return ESP_OK;  /* 已经在运行 */
    }
    
    /* 0 = 尚未收到控制 JSON，避免上电数百毫秒误触发超时停车 */
    s_last_cmd_time_ms = 0;
    s_timeout_stopped = false;

    return app_task_start(APP_TASK_RC_WATCHDOG, rc_watchdog_task, NULL, &s_watchdog_handle);
}

void remote_control_watchdog_stop(void)
{
    s_watchdog_running = false;
    remote_control_stop_notify_end();
}

static void update_last_cmd_time(void)
{
    if (s_timeout_stopped) {
        remote_control_stop_notify_end();
    }
    s_timeout_stopped = false;
    s_last_cmd_time_ms = get_current_time_ms();
}

void remote_control_note_rx_activity(void)
{
    /*
     * UART/USB 可能把一条 JSON 拆成多段。已有控制会话时，收到后续片段也刷新
     * 看门狗时间，避免“第二段刚到但尚未 parse 完整”时误停车。
     */
    if (s_last_cmd_time_ms != 0 && !s_timeout_stopped) {
        s_last_cmd_time_ms = get_current_time_ms();
    }
}

void remote_control_link_reset(void)
{
    remote_control_stop_notify_end();
    if (ota_binary_stream_is_active()) {
        ota_binary_stream_abort();
    }
    ota_offer_disarm();
    s_last_cmd_time_ms = 0;
    s_timeout_stopped = true;
    rc_vehicle_stop();
    ESP_LOGI(tag_s, "链路会话已清空（OTA 中止、停车）");
}

void remote_control_apply_json_root(cJSON *root, const char *json_raw)
{
    if (!root) {
        return;
    }

    cJSON *head = cJSON_GetObjectItem(root, "head");
    if (!head || !cJSON_IsObject(head)) {
        /* 安全收敛：无 head 的 JSON 不再进入 OTA 分支，避免误触发升级。 */
        (void)json_raw;
        return;
    }

    cJSON *m_type = cJSON_GetObjectItem(head, "message_type");
    if (m_type && cJSON_IsNumber(m_type) && m_type->valueint == REMOTE_CTRL_MSG_LINK_RESET) {
        remote_control_link_reset();
        return;
    }

    update_last_cmd_time();

    if (m_type && cJSON_IsNumber(m_type) && m_type->valueint == 1) {
        ESP_LOGW(tag_s, "message_type=1: reboot (all devices)");
        esp_restart();
        return;
    }

    cJSON *body = cJSON_GetObjectItem(root, "body");
    if (m_type && cJSON_IsNumber(m_type) && body) {
        if (m_type->valueint == 4) {
            cJSON *pin_ctrl = cJSON_GetObjectItem(body, "pin_setctrl");
            if (pin_ctrl && cJSON_IsObject(pin_ctrl)) {
                cJSON *jp = cJSON_GetObjectItem(pin_ctrl, "pin");
                cJSON *jv = cJSON_GetObjectItem(pin_ctrl, "val");
                if (jp && jv && cJSON_IsNumber(jp) && cJSON_IsNumber(jv)) {
                    int pin = jp->valueint;
                    int val = jv->valueint;
                    ESP_LOGI(tag_s, "GPIO: pin %d -> %d", pin, val);
                    rc_vehicle_shot(pin, val);
                }
            }
        } else if (m_type->valueint == 3) {
            cJSON *pwm_ctrl = cJSON_GetObjectItem(body, "pwm_ctrl");
            if (remote_control_is_stop_pwm_json(pwm_ctrl)) {
                rc_vehicle_stop();
                ESP_LOGD(tag_s, "停车 JSON (speed=0,direction=0)");
                return;
            }
            if (pwm_ctrl && cJSON_IsObject(pwm_ctrl)) {
                cJSON *jm = cJSON_GetObjectItem(pwm_ctrl, "mode");
                cJSON *jt = cJSON_GetObjectItem(pwm_ctrl, "type");
                cJSON *jv = cJSON_GetObjectItem(pwm_ctrl, "val");
                cJSON *js = cJSON_GetObjectItem(pwm_ctrl, "speed");
                cJSON *jd = cJSON_GetObjectItem(pwm_ctrl, "direction");
                if (jm && jv && cJSON_IsNumber(jm) && cJSON_IsNumber(jv)) {
                    int mode = jm->valueint;
                    int type = jt && cJSON_IsNumber(jt) ? jt->valueint : 0;
                    int val = jv->valueint;
                    int speed = js && cJSON_IsNumber(js) ? js->valueint : val;
                    int steer = jd && cJSON_IsNumber(jd) ? jd->valueint : ((mode >= 5) ? val : 0);
                    if (mode == 0 && val == 0) {
                        rc_vehicle_stop();
                        ESP_LOGD(tag_s, "PWM mode=0 val=0 -> stop");
                        return;
                    }
                    ESP_LOGI(tag_s, "PWM: mode %d type %d speed %d steer %d", mode, type, speed, steer);
                    rc_vehicle_control(mode, speed, steer);
                }
            }
        }
    }
}

void remote_control_apply_json(const char *json)
{
    if (!json) {
        return;
    }

    cJSON *root = cJSON_Parse(json);
    if (!root) {
        ESP_LOGW(tag_s, "JSON parse failed (len=%u)", (unsigned)strlen(json));
        return;
    }

    remote_control_apply_json_root(root, json);
    cJSON_Delete(root);
}
