#include "link_uart.h"
#include "remote_control.h"
#include "wifidevnum_config.h"
#include "device_nvs.h"
#include "rc_pwm_control.h"
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "link_dma_uart.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#include "sdkconfig.h"

#include "core/build_config.h"
#include "task_manager.h"

#if BUILD_IS_DEBUG
/* Debug 固定仅打印 JSON 级日志；RAW 日志关闭，避免刷屏 */
#define UART_LINK_LOG_TX_JSON 1
#define UART_LINK_LOG_TX_RAW  0
#define UART_LINK_LOG_RX_JSON 1
#define UART_LINK_LOG_RX_RAW  0
#else
#define UART_LINK_LOG_TX_JSON 0
#define UART_LINK_LOG_TX_RAW  0
#define UART_LINK_LOG_RX_JSON 0
#define UART_LINK_LOG_RX_RAW  0
#endif
#include "heart_payload.h"
#include "ota_offer_protocol.h"
#include "ota_frame_protocol.h"

/* OTA 二进制流模块 */
#include "ota_binary_stream.h"
#include "cJSON.h"

static const char *tag_s = "LINK_UART";

/* UART 配置 */
#define UART_LINK_PORT          UART_NUM_1

/* JSON 接收缓冲 */
#define UART_JSON_MAX_BYTES     REMOTE_CTRL_JSON_MAX_BYTES

/* 心跳周期 */
#ifndef UART_HEART_PERIOD_MS
#define UART_HEART_PERIOD_MS    3000
#endif

/* 心跳：对端曾 RX 且在此窗口内（允许 App 只收不发） */
#define UART_LINK_ALIVE_MS      (UART_HEART_PERIOD_MS + 5000)
/* 拔线/无字节：超过此时间无 RX 即停停车推送（约 3～4 条 @300ms） */
#define UART_PEER_UNPLUG_MS     1200

/* 协议帧分隔符（使用换行符作为 JSON 分隔） */
#define UART_FRAME_DELIMITER    '\n'

/* 函数前向声明 */
static int uart_link_send_json_line(const char *json);
static void uart_send_heartbeat_once(void);

static TaskHandle_t rx_task_handle_s = NULL;
static TaskHandle_t hb_task_handle_s = NULL;
static volatile bool link_up_s = false;
/** 上次从 UART1 RX 收到对端数据的时刻；0=从未收到 */
static volatile uint32_t last_peer_rx_ms = 0;

/* 发送互斥 */
static SemaphoreHandle_t tx_mutex_s = NULL;

/* 接收 JSON 缓冲区 */
static char rx_json_buf_s[UART_JSON_MAX_BYTES + 1];
static size_t rx_buf_pos_s = 0;

/* OTA 状态缓冲 */
static char last_ota_stat_json_s[192];
static bool uart_ota_framed_mode_s = false;

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

/* UART OTA 状态：仅缓存供调试，不向串口发 JSON（固件数据按字节回传） */
static void uart_ota_status_callback(const char *json)
{
    if (json == NULL) {
        return;
    }
    strncpy(last_ota_stat_json_s, json, sizeof(last_ota_stat_json_s) - 1);
    last_ota_stat_json_s[sizeof(last_ota_stat_json_s) - 1] = '\0';
}

/* UART OTA 初始化 */
static void uart_ota_init(void)
{
    /* 初始化通用 OTA 模块 */
    esp_err_t err = ota_binary_stream_init(OTA_TRANSPORT_UART, uart_ota_status_callback, NULL);
    if (err != ESP_OK) {
        ESP_LOGE(tag_s, "UART OTA init failed: %s", esp_err_to_name(err));
    } else {
        ESP_LOGI(tag_s, "UART OTA initialized");
    }
}

static const char *uart_tx_json_kind_hint(const char *json)
{
    if (!json) {
        return "JSON";
    }
    if (strstr(json, "\"message_type\":1002") != NULL) {
        return "OTA(1002)";
    }
    if (strstr(json, "\"message_type\":1}") != NULL ||
        strstr(json, "\"message_type\": 1}") != NULL ||
        strstr(json, "\"message_type\":1,") != NULL ||
        strstr(json, "\"message_type\": 1,") != NULL) {
        return "心跳(1)";
    }
    if (strstr(json, "\"message_type\":3") != NULL) {
        return "停车/状态(3)";
    }
    if (strstr(json, "\"message_type\":4") != NULL || strstr(json, "\"message_type\":5") != NULL) {
        return "告警/错误(4/5)";
    }
    return "JSON";
}

static void uart_link_log_tx_json(const char *json)
{
#if UART_LINK_LOG_TX_JSON
    if (json) {
        ESP_LOGI(tag_s, "UART1 TX %s: %s", uart_tx_json_kind_hint(json), json);
    }
#else
    (void)json;
#endif
}

static void uart_link_log_tx_raw(const uint8_t *data, size_t len)
{
#if UART_LINK_LOG_TX_RAW
    if (!data || len == 0) {
        return;
    }
    /* 仅打印长度和 ASCII 预览，省略 hex dump 便于阅读 */
    char ascii[65];
    size_t n = len > sizeof(ascii) - 1 ? sizeof(ascii) - 1 : len;
    for (size_t i = 0; i < n; i++) {
        uint8_t c = data[i];
        ascii[i] = (c >= 32 && c < 127) ? (char)c : '.';
    }
    ascii[n] = '\0';
    if (len > n) {
        ESP_LOGI(tag_s, "UART1 TX %u bytes: \"%s\"...", (unsigned)len, ascii);
    } else {
        ESP_LOGI(tag_s, "UART1 TX %u bytes: \"%s\"", (unsigned)len, ascii);
    }
#else
    (void)data;
    (void)len;
#endif
}

static int uart_link_send_internal(const uint8_t *data, size_t len)
{
    if (!data || len == 0) {
        return -1;
    }
    return link_dma_uart_send(data, len, 200);
}

static bool uart_link_tx_acquire(TickType_t timeout)
{
    if (tx_mutex_s == NULL) {
        return false;
    }
    if (xSemaphoreTake(tx_mutex_s, timeout) != pdTRUE) {
        ESP_LOGW(tag_s, "UART1 TX 互斥等待超时");
        return false;
    }
    return true;
}

static void uart_link_tx_release(void)
{
    if (tx_mutex_s != NULL) {
        xSemaphoreGive(tx_mutex_s);
    }
}

static int uart_link_tx(const uint8_t *data, size_t len, TickType_t mutex_timeout)
{
    if (!data || len == 0) {
        return -1;
    }
    if (!uart_link_tx_acquire(mutex_timeout)) {
        return -2;
    }
    int ret = uart_link_send_internal(data, len);
    if (ret > 0) {
        uart_link_log_tx_raw(data, len);
    }
    uart_link_tx_release();
    return ret;
}

static void uart_link_note_peer_rx(void)
{
    last_peer_rx_ms = get_current_time_ms();
    link_up_s = true;
}

int link_uart_send_raw(const uint8_t *data, size_t len)
{
    return uart_link_tx(data, len, portMAX_DELAY);
}

static int uart_link_send_json_line(const char *json)
{
    if (!json) {
        return -1;
    }

    size_t len = strlen(json);
    if (len == 0 || len > 1024) {
        return -1;
    }

    if (!uart_link_tx_acquire(portMAX_DELAY)) {
        return -2;
    }

    int ret = uart_link_send_internal((const uint8_t *)json, len);
    if (ret > 0) {
        ret += uart_link_send_internal((const uint8_t *)"\n", 1);
        uart_link_log_tx_json(json);
    }

    uart_link_tx_release();
    return ret;
}

int link_uart_send_heartbeat(const char *json)
{
    return uart_link_send_json_line(json);
}

int link_uart_send_log(const char *json)
{
    return uart_link_send_json_line(json);
}

static bool uart_peer_rx_recent(void)
{
    uint32_t last = last_peer_rx_ms;
    if (last == 0) {
        return false;
    }
    return (get_current_time_ms() - last) < UART_PEER_UNPLUG_MS;
}

bool link_uart_has_peer_seen(void)
{
    return last_peer_rx_ms != 0;
}

bool link_uart_is_connected(void)
{
    uint32_t last = last_peer_rx_ms;
    if (last == 0) {
        return false;
    }
    return (get_current_time_ms() - last) < UART_LINK_ALIVE_MS;
}

static void uart_send_ota_offer_json(const char *json)
{
    if (!json) {
        return;
    }
    (void)uart_link_send_json_line(json);
}

static void uart_ota_done_report(bool success, uint32_t bytes_written, const char *err_step)
{
    char devid[32] = {0};
    read_from_nvs(DEVICE_CFG_KEY_DEVICE_ID, devid, sizeof(devid));

    char *json = ota_offer_result_json_malloc(devid, success, bytes_written, err_step);
    if (json) {
        uart_send_ota_offer_json(json);
        free(json);
    }
}

/** 手机 message_type=6：回复 OTA 询问（每次重连串口可各发一次） */
static void uart_send_ota_query_on_link_ready(void)
{
    char devid[32] = {0};
    read_from_nvs(DEVICE_CFG_KEY_DEVICE_ID, devid, sizeof(devid));

    char *json = ota_offer_query_json_malloc(devid);
    if (json) {
        uart_send_ota_offer_json(json);
        free(json);
    }
}

static void link_uart_session_reset(void)
{
    rx_buf_pos_s = 0;
    uart_ota_framed_mode_s = false;
    ota_frame_protocol_reset();
    remote_control_link_reset();
}

/* 处理接收到的 JSON（root 已由调用方解析） */
static void process_received_json(cJSON *root, const char *json)
{
    if (!root || !json || json[0] == '\0') {
        return;
    }

#if UART_LINK_LOG_RX_JSON
    ESP_LOGI(tag_s, "UART1 RX JSON line: %s", json);
#endif

    cJSON *head = cJSON_GetObjectItem(root, "head");
    cJSON *m_type = (head && cJSON_IsObject(head)) ? cJSON_GetObjectItem(head, "message_type") : NULL;

    if (m_type && cJSON_IsNumber(m_type) && m_type->valueint == REMOTE_CTRL_MSG_LINK_RESET) {
        link_uart_session_reset();
        ESP_LOGI(tag_s, "收到 message_type=2，已清空 UART 会话");
        return;
    }

    if (m_type && cJSON_IsNumber(m_type) && (int)m_type->valuedouble == REMOTE_CTRL_MSG_LINK_READY) {
        ESP_LOGI(tag_s, "UART1 收到 link_ready(6)，回复 OTA 询问 1002/query + 首条心跳");
        uart_send_ota_query_on_link_ready();
        uart_send_heartbeat_once();
        return;
    }

    if (ota_offer_parse_phone_response(root)) {
        ESP_LOGI(tag_s, "手机同意 OTA (head.message_type=0)，可发 0x01/固件字节");
        return;
    }

    remote_control_apply_json_root(root, json);
}

static void uart_ota_tx_frame(const uint8_t *frame, uint16_t len, void *ctx)
{
    (void)ctx;
    if (frame != NULL && len > 0) {
        (void)uart_link_tx(frame, len, portMAX_DELAY);
    }
}

/* 旧版裸字节回显（无 OT 帧头时兼容） */
static void uart_link_ota_ack_payload(const uint8_t *payload, uint16_t payload_len)
{
    for (uint16_t i = 0; i < payload_len; i++) {
        uint8_t b = payload[i];
        (void)uart_link_tx(&b, 1, portMAX_DELAY);
    }
}

/* 逐字节写入 OTA（单字节 0x02 包）并回传确认 */
static void uart_link_ota_feed_raw_bytes(const uint8_t *data, int len)
{
    for (int i = 0; i < len; i++) {
        uint8_t pkt[2] = { OTA_OP_DATA, data[i] };
        if (ota_binary_stream_process_packet(pkt, 2) == 0) {
            uint8_t b = data[i];
            (void)uart_link_tx(&b, 1, portMAX_DELAY);
        }
    }
}

/* 处理 OTA 数据包（0x01/0x02/0x03）；仅 0x02 固件载荷按字节回传 */
static void process_ota_packet(const uint8_t *data, uint16_t len)
{
    if (!data || len == 0) {
        return;
    }

    if (ota_frame_rx_looks_framed(data, len)) {
        ota_frame_rx_feed(data, len);
        return;
    }

    uint8_t op = data[0];
    int rc = ota_binary_stream_process_packet(data, len);

    if (rc == 0 && op == OTA_OP_DATA && len > 1) {
        uart_link_ota_ack_payload(data + 1, (uint16_t)(len - 1));
    }
}

/* 检查数据是否为 OTA 命令（以 0x01/0x02/0x03 开头） */
static bool is_ota_command(uint8_t first_byte)
{
    return (first_byte == 0x01 || first_byte == 0x02 || first_byte == 0x03);
}

static bool uart_rx_json_balanced(const char *s)
{
    int depth = 0;

    if (!s || s[0] != '{') {
        return false;
    }

    for (; *s != '\0'; s++) {
        if (*s == '{') {
            depth++;
        } else if (*s == '}') {
            depth--;
            if (depth < 0) {
                return false;
            }
        }
    }
    return depth == 0;
}

static void uart_rx_try_finish_json_line(void)
{
    if (rx_buf_pos_s == 0) {
        return;
    }
    rx_json_buf_s[rx_buf_pos_s] = '\0';

    if (rx_json_buf_s[0] != '{') {
        ESP_LOGW(tag_s, "UART1 RX 行结束但非 JSON (len=%u)", (unsigned)rx_buf_pos_s);
        rx_buf_pos_s = 0;
        return;
    }

    cJSON *root = cJSON_Parse(rx_json_buf_s);
    if (!root) {
        if (!uart_rx_json_balanced(rx_json_buf_s)) {
            /* 多行/分段 JSON 尚未收齐，继续累积 */
            return;
        }
        ESP_LOGW(tag_s, "UART1 RX JSON 无效 (len=%u)", (unsigned)rx_buf_pos_s);
        rx_buf_pos_s = 0;
        return;
    }

    process_received_json(root, rx_json_buf_s);
    cJSON_Delete(root);
    rx_buf_pos_s = 0;
}

/* 原始接收调试：本函数用 ESP_LOG 打印；输出在「控制台 UART」(默认 UART0/USB)，与 UART1 业务引脚可能相同，见文档 §1.5 */
static void log_uart1_rx_raw(const uint8_t *data, int len)
{
#if UART_LINK_LOG_RX_RAW
    if (!data || len <= 0) {
        return;
    }
    /* 仅打印长度和 ASCII 内容，省略 hex dump 便于阅读 JSON */
    char ascii[129];
    int n = len > (int)sizeof(ascii) - 1 ? (int)sizeof(ascii) - 1 : len;
    for (int i = 0; i < n; i++) {
        uint8_t c = data[i];
        ascii[i] = (c >= 32 && c < 127) ? (char)c : '.';
    }
    ascii[n] = '\0';
    if (len > n) {
        ESP_LOGI(tag_s, "UART1 RX %d bytes: \"%s\"...", len, ascii);
    } else {
        ESP_LOGI(tag_s, "UART1 RX %d bytes: \"%s\"", len, ascii);
    }
#else
    (void)data;
    (void)len;
#endif
}

/** 处理 JSON 行后的 OT 帧 / 裸 OTA 字节（同一 uart_read 内粘包） */
static void link_uart_feed_rx_binary_chunk(const uint8_t *data, int len)
{
    if (!data || len <= 0) {
        return;
    }

    bool frame_parser_active = ota_frame_rx_is_active();
    bool frame_header_complete = (len >= 2 && data[0] == OTA_FRAME_MAGIC0 && data[1] == OTA_FRAME_MAGIC1);
    if (uart_ota_framed_mode_s || frame_parser_active || ota_frame_rx_looks_framed(data, len)) {
        if (!ota_offer_is_armed() && !ota_binary_stream_is_active()) {
            ESP_LOGW(tag_s, "UART1 忽略 OTA 二进制：尚未收到手机 message_type=0");
            return;
        }
        if (uart_ota_framed_mode_s || frame_parser_active || frame_header_complete) {
            uart_ota_framed_mode_s = true;
        }
        ESP_LOGI(tag_s, "UART1 OTA frame bytes (%d bytes)", len);
        ota_frame_rx_feed(data, (uint16_t)len);
        return;
    }

    if (is_ota_command(data[0])) {
        if (!ota_offer_is_armed() && !ota_binary_stream_is_active()) {
            ESP_LOGW(tag_s, "UART1 忽略 OTA 二进制：尚未收到手机 message_type=0");
            return;
        }
        ESP_LOGI(tag_s, "UART1 legacy OTA packet (%d bytes)", len);
        process_ota_packet(data, len);
        return;
    }

    if (ota_binary_stream_is_active() || ota_offer_is_armed()) {
        uart_link_ota_feed_raw_bytes(data, len);
    }
}

static void uart_skip_json_delimiters_before_ota(const uint8_t **data, int *len)
{
    while (*len > 0 && ((*data)[0] == '\r' || (*data)[0] == '\n')) {
        (*data)++;
        (*len)--;
    }
}

static void link_uart_feed_rx_bytes(const uint8_t *data, int len)
{
    if (!data || len <= 0) {
        return;
    }

    uart_link_note_peer_rx();
    log_uart1_rx_raw(data, len);

    if (rx_buf_pos_s > 0 || data[0] == '{') {
        remote_control_note_rx_activity();
    }

    /*
     * Android/USB 串口库可能发送 "\r\n" 后紧跟 OT 帧，甚至把 '\r' 和 '\n'
     * 拆成两次 read。OTA 尚未 begin 时忽略这些 JSON 行尾，避免把 '\n'
     * 当成第一字节固件数据写入 flash。
     */
    if (rx_buf_pos_s == 0 && ota_offer_is_armed() && !ota_binary_stream_is_active()) {
        uart_skip_json_delimiters_before_ota(&data, &len);
        if (len <= 0) {
            return;
        }
    }

    /* 整包即为 OT 帧或裸 0x01/0x02/0x03（不以 { 开头） */
    if (data[0] != '{' &&
        (ota_frame_rx_looks_framed(data, len) || is_ota_command(data[0]))) {
        link_uart_feed_rx_binary_chunk(data, len);
        return;
    }

    if (data[0] != '{' && (ota_binary_stream_is_active() || ota_offer_is_armed())) {
        link_uart_feed_rx_binary_chunk(data, len);
        return;
    }

    for (int i = 0; i < len; i++) {
        uint8_t ch = data[i];

        if (is_ota_command(ch) && i == 0 && rx_buf_pos_s == 0) {
            link_uart_feed_rx_binary_chunk(data + i, len - i);
            break;
        }

        if (ch == UART_FRAME_DELIMITER || ch == '\r') {
            uart_rx_try_finish_json_line();
            /* 同一 read 内：{"...":0}\nOT... 须在 JSON 行后立刻吃二进制尾 */
            int tail = i + 1;
            if (tail < len) {
                const uint8_t *tail_data = data + tail;
                int tail_len = len - tail;
                uart_skip_json_delimiters_before_ota(&tail_data, &tail_len);
                link_uart_feed_rx_binary_chunk(tail_data, tail_len);
                return;
            }
        } else if (rx_buf_pos_s < UART_JSON_MAX_BYTES) {
            rx_json_buf_s[rx_buf_pos_s++] = ch;
        } else {
            ESP_LOGW(tag_s, "UART1 RX JSON buffer overflow");
            rx_buf_pos_s = 0;
        }
    }
}

static void uart_dma_rx_cb(const uint8_t *data, size_t len, void *ctx)
{
    (void)ctx;
    link_uart_feed_rx_bytes(data, (int)len);
}

static void uart_link_watchdog_task(void *param)
{
    (void)param;
    ESP_LOGI(tag_s, "UART1 DMA RX 监视任务 (port=%d, %d baud, TX=GPIO%d RX=GPIO%d)",
             (int)UART_LINK_PORT, CONFIG_APP_UART_LINK_BAUDRATE,
             CONFIG_APP_UART_LINK_TX_GPIO, CONFIG_APP_UART_LINK_RX_GPIO);

    while (1) {
        vTaskDelay(pdMS_TO_TICKS(200));
        uint32_t now = get_current_time_ms();
        uint32_t last_rx = last_peer_rx_ms;
        if (last_rx != 0 && (now - last_rx) > UART_PEER_UNPLUG_MS) {
            if (link_up_s) {
                link_up_s = false;
                remote_control_stop_notify_end();
                if (ota_offer_is_armed() || ota_binary_stream_is_active() ||
                    ota_frame_rx_is_active() || rx_buf_pos_s > 0) {
                    link_uart_session_reset();
                    ESP_LOGW(tag_s, "UART1 RX 超时，已清空未完成 OTA/JSON 会话");
                }
                ESP_LOGI(tag_s, "UART1 对端近期无 RX（%d ms），停止停车推送", UART_PEER_UNPLUG_MS);
            }
        }
    }
}

static void uart_send_heartbeat_once(void)
{
    if (!link_uart_has_peer_seen()) {
        return;
    }

    char devid[32] = {0};
    read_from_nvs(DEVICE_CFG_KEY_DEVICE_ID, devid, sizeof(devid));
    char *heartbeat = heart_payload_json_malloc(devid, NULL, false);
    if (!heartbeat) {
        return;
    }
    (void)link_uart_send_heartbeat(heartbeat);
    free(heartbeat);
}

static void uart_stop_notify_cb(const char *json)
{
    if (json == NULL || !uart_peer_rx_recent()) {
        return;
    }
    (void)uart_link_send_json_line(json);
}

static void uart_heartbeat_task(void *param)
{
    (void)param;
    ESP_LOGI(tag_s, "UART1 心跳任务已启动 (周期 %d ms)", UART_HEART_PERIOD_MS);
    for (uint32_t beat_count = 1;; beat_count++) {
        uart_send_heartbeat_once();
        ESP_LOGD(tag_s, "UART1 心跳 #%lu", (unsigned long)beat_count);
        vTaskDelay(pdMS_TO_TICKS(UART_HEART_PERIOD_MS));
    }
}

static esp_err_t uart_link_init_hardware(void)
{
    int tx_gpio = CONFIG_APP_UART_LINK_TX_GPIO;
    int rx_gpio = CONFIG_APP_UART_LINK_RX_GPIO;

    if (tx_gpio < 0 || rx_gpio < 0) {
        ESP_LOGE(tag_s, "Invalid UART GPIO config: TX=%d, RX=%d", tx_gpio, rx_gpio);
        return ESP_ERR_INVALID_ARG;
    }

    rx_buf_pos_s = 0;

    esp_err_t err = link_dma_uart_start(UART_LINK_PORT, CONFIG_APP_UART_LINK_BAUDRATE,
                                        tx_gpio, rx_gpio, uart_dma_rx_cb, NULL);
    if (err != ESP_OK) {
        ESP_LOGE(tag_s, "UART DMA init failed: %s", esp_err_to_name(err));
        return err;
    }

    ESP_LOGI(tag_s, "UART1 DMA 就绪: UART_NUM_%d TX=GPIO%d RX=GPIO%d %d baud",
             (int)UART_LINK_PORT, tx_gpio, rx_gpio, CONFIG_APP_UART_LINK_BAUDRATE);
    return ESP_OK;
}

esp_err_t link_uart_start(void)
{
    ESP_LOGI(tag_s, "启动 UART 链路 (GPIO%d TX / GPIO%d RX, %d baud)",
             CONFIG_APP_UART_LINK_TX_GPIO, CONFIG_APP_UART_LINK_RX_GPIO,
             CONFIG_APP_UART_LINK_BAUDRATE);

    ota_offer_reset_boot();
    uart_ota_framed_mode_s = false;
    ota_offer_set_done_callback(uart_ota_done_report);
    remote_control_set_stop_notify_cb(uart_stop_notify_cb);
    ota_frame_protocol_init(uart_ota_tx_frame, NULL);

    /* 初始化 UART OTA 模块 */
    uart_ota_init();

    /* 初始化 UART 硬件 */
    esp_err_t err = uart_link_init_hardware();
    if (err != ESP_OK) {
        return err;
    }

    /* 创建发送互斥锁 */
    tx_mutex_s = xSemaphoreCreateMutex();
    if (!tx_mutex_s) {
        ESP_LOGE(tag_s, "TX mutex create failed");
        link_dma_uart_stop();
        return ESP_ERR_NO_MEM;
    }

    /* 启动遥控看门狗（与 BLE 模式相同） */
    remote_control_watchdog_start();

    /* DMA 接收由 link_dma_uart 驱动；独立任务仅做对端离线检测 */
    BaseType_t ret = xTaskCreate(uart_link_watchdog_task, "uart_wd",
                                 3072, NULL, 5, &rx_task_handle_s);
    if (ret != pdPASS) {
        ESP_LOGE(tag_s, "UART watchdog task create failed");
        vSemaphoreDelete(tx_mutex_s);
        tx_mutex_s = NULL;
        link_dma_uart_stop();
        return ESP_ERR_NO_MEM;
    }

    /* 启动心跳任务 */
    ret = xTaskCreate(uart_heartbeat_task, "uart_hb",
                      4096, NULL, 5, &hb_task_handle_s);
    if (ret != pdPASS) {
        ESP_LOGE(tag_s, "Heartbeat task create failed");
        vTaskDelete(rx_task_handle_s);
        rx_task_handle_s = NULL;
        vSemaphoreDelete(tx_mutex_s);
        tx_mutex_s = NULL;
        link_dma_uart_stop();
        return ESP_ERR_NO_MEM;
    }

    link_up_s = false;
    last_peer_rx_ms = 0;

    ESP_LOGI(tag_s, "UART 链路启动成功 (事件驱动收发；无新日志=无收发)");
    ESP_LOGI(tag_s, "握手: 设备不主动发 1002；须手机经 UART1 发 message_type=6 后才回 1002/query");
#if BUILD_IS_DEBUG
    ESP_LOGI(tag_s, "Debug: UART1 收发 JSON/二进制均有 I 级日志 (监视器 COM=UART0，数据口=GPIO17/18)");
#endif
    ESP_LOGI(tag_s, "串口工具应接 UART1: 手机_TX->ESP_GPIO%d(RX), 手机_RX<-ESP_GPIO%d(TX), %d baud",
             CONFIG_APP_UART_LINK_RX_GPIO, CONFIG_APP_UART_LINK_TX_GPIO,
             CONFIG_APP_UART_LINK_BAUDRATE);
    ESP_LOGI(tag_s, "手机发 link_ready(6) 后设备回 OTA 询问(1002)；手机回 0 后发 OT 帧固件");
    return ESP_OK;
}

void link_uart_stop(void)
{
    /* 停止心跳任务 */
    if (hb_task_handle_s) {
        vTaskDelete(hb_task_handle_s);
        hb_task_handle_s = NULL;
    }

    /* 停止接收任务 */
    if (rx_task_handle_s) {
        vTaskDelete(rx_task_handle_s);
        rx_task_handle_s = NULL;
    }

    /* 停止遥控看门狗 */
    remote_control_watchdog_stop();

    /* 删除互斥锁 */
    if (tx_mutex_s) {
        vSemaphoreDelete(tx_mutex_s);
        tx_mutex_s = NULL;
    }

    link_dma_uart_stop();

    link_up_s = false;

    ESP_LOGI(tag_s, "UART 链路已停止");
}
