/**
 * XYZW WebSocket 客户端
 * 基于 readable-xyzw-ws.js 重构，适配本项目架构
 */

import { bonProtocol, g_utils } from './bonProtocol.js'

/** 生成 [min,max] 的随机整数 */
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min

/** Promise 版 sleep */
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))

/** 日志配置检查 */
const shouldLog = (type) => {
  try {
    const config = JSON.parse(localStorage.getItem('batchTaskLogConfig') || '{}')
    return config[type] === true
  } catch {
    return false
  }
}

/**
 * 命令注册器：保存每个 cmd 的默认体，发送时与 params 合并
 */
export class CommandRegistry {
  constructor(encoder, enc) {
    this.encoder = encoder
    this.enc = enc
    this.commands = new Map()
  }

  /** 注册命令 */
  register(cmd, defaultBody = {}) {
    this.commands.set(cmd, (ack = 0, seq = 0, params = {}) => ({
      cmd,
      ack,
      seq,
      code: 0,
      rtt: randInt(0, 500),
      time: Date.now(),
      body: this.encoder?.bon?.encode
        ? this.encoder.bon.encode({ ...defaultBody, ...params })
        : undefined,
      c: undefined,
      hint: undefined,
    }))
    return this
  }

  /** 特例：系统心跳的 ack 用的是 "_sys/ack" */
  registerHeartbeat() {
    this.commands.set("heart_beat", (ack, seq) => ({
      cmd: "_sys/ack",
      ack,
      seq,
      time: Date.now(),
      body: undefined,
      c: undefined,
      hint: undefined,
    }))
    return this
  }

  /** 生成最终可发送的二进制 */
  encodePacket(raw) {
    if (this.encoder?.encode && this.enc) {
      // 使用加密编码
      return this.encoder.encode(raw, this.enc)
    } else {
      // 降级到JSON字符串
      return JSON.stringify(raw)
    }
  }

  /** 构造报文 */
  build(cmd, ack, seq, params) {
    const fn = this.commands.get(cmd)
    if (!fn) throw new Error(`Unknown cmd: ${cmd}`)
    return fn(ack, seq, params)
  }
}

/** 预注册游戏命令 */
export function registerDefaultCommands(reg) {
  return reg.registerHeartbeat()
    // 角色/系统
    .register("role_getroleinfo", {
      clientVersion: "1.65.3-wx",
      inviteUid: 0,
      platform: "hortor",
      platformExt: "mix",
      scene: "",
    })
    .register("system_getdatabundlever", { isAudit: false })
    .register("system_buygold", { buyNum: 1 })
    .register("system_claimhangupreward")
    .register("system_signinreward")
    .register("system_mysharecallback", { isSkipShareCard: true, type: 2 })

    // 任务相关
    .register("task_claimdailypoint", { taskId: 1 })
    .register("task_claimdailyreward", { rewardId: 0 })
    .register("task_claimweekreward", { rewardId: 0 })

    // 好友/招募
    .register("friend_batch", { friendId: 0 })
    .register("hero_recruit", { byClub: false, recruitNumber: 1, recruitType: 3 })
    .register("hero_heroupgradestar", { heroId: 0 })
    .register("book_upgrade", { heroId: 0 })
    .register("book_claimpointreward", {})
    .register("item_openbox", { itemId: 2001, number: 10 })

    // 竞技场
    .register("arena_startarea")
    .register("arena_getareatarget", { refresh: false })
    .register("fight_startareaarena", { targetId: 530479307 })
    .register("arena_getarearank", { rankType: 0 })

    // 商店
    .register("store_goodslist", { storeId: 1 })
    .register("store_buy", { goodsId: 1 })
    .register("store_purchase", { goodsId: 1 })
    .register("store_refresh", { storeId: 1 })

    // 军团
    .register("legion_getinfo")
    .register("legionwar_getdetails", {})
    .register("legion_signin")
    .register("legion_getwarrank")

    // 邮件
    .register("mail_getlist", { category: [0, 4, 5], lastId: 0, size: 60 })
    .register("mail_claimallattachment", { category: 0 })

    // 学习问答
    .register("study_startgame")
    .register("study_answer")
    .register("study_claimreward", { rewardId: 1 })

    // 战斗相关
    .register("fight_starttower")
    .register("fight_startboss")
    .register("fight_startlegionboss")
    .register("fight_startdungeon")
    .register("fight_startpvp")

    // 瓶子机器人
    .register("bottlehelper_claim")
    .register("bottlehelper_start", { bottleType: -1 })
    .register("bottlehelper_stop", { bottleType: -1 })

    // 军团匹配和签到
    .register("legionmatch_rolesignup")
    .register("legion_signin")

    // 神器抽奖
    .register("artifact_lottery", { lotteryNumber: 1, newFree: true, type: 1 })

    // 月度活动
    .register("activity_get", {})
    .register("monthlyactivity_receivereward", {})

    // 灯神相关
    .register("genie_sweep", { genieId: 1 })
    .register("genie_buysweep")

    // 礼包相关
    .register("discount_claimreward", { discountId: 1 })
    .register("card_claimreward", { cardId: 1 })

    // 爬塔相关
    .register("tower_getinfo")
    .register("tower_claimreward")

    // 队伍相关
    .register("presetteam_getinfo")
    .register("presetteam_getinfo")
    .register("presetteam_setteam")
    .register("presetteam_changeteam", { teamId: 1 })
    .register("presetteam_saveteam", { teamId: 1 })
    .register("role_gettargetteam")

    // 排名相关
    .register("rank_getroleinfo")

    // 梦魇相关
    .register("nightmare_getroleinfo")

    // 赛车相关
    .register("car_getrolecar")
    .register("car_refresh", { carId: "" })
    .register("car_claim", { carId: "" })
    .register("car_send", { carId: "", helperId: 0, text: "" })
}

/**
 * XYZW WebSocket 客户端
 */
export class XyzwWebSocketClient {
  constructor({ url, utils, heartbeatMs = 5000, idleTimeout = 30000 }) {
    this.url = url
    this.utils = utils || g_utils
    this.enc = this.utils?.getEnc ? this.utils.getEnc("auto") : undefined

    this.socket = null
    this.ack = 1
    this.seq = 0
    this.sendQueue = []
    this.sendQueueTimer = null
    this.heartbeatTimer = null
    this.heartbeatInterval = heartbeatMs

    this.dialogStatus = false
    this.messageListener = null
    this.showMsg = false
    this.connected = false

    this.promises = Object.create(null)
    this.registry = registerDefaultCommands(new CommandRegistry(this.utils, this.enc))

    // WebSocket客户端初始化

    // 状态回调
    this.onConnect = null
    this.onDisconnect = null
    this.onError = null
    
    // 🆕 v3.13.5: 空闲超时配置
    this.idleTimeout = idleTimeout  // 空闲超时时间（默认30秒）
    this.lastActivityTime = Date.now()  // 最后活动时间
    this.idleTimer = null  // 空闲检测定时器
  }

  /** 初始化连接 */
  init() {
    if (shouldLog('websocket')) console.log(`🔗 连接: ${this.url.split('?')[0]}`)

    this.socket = new WebSocket(this.url)

    this.socket.onopen = () => {
      if (shouldLog('websocket')) console.log(`✅ 连接成功`)
      this.connected = true
      // 启动心跳机制
      this._setupHeartbeat()
      // 启动消息队列处理
      this._processQueueLoop()
      // 🆕 v3.13.5: 启动空闲超时检测
      this._startIdleTimeout()
      if (this.onConnect) this.onConnect()
    }

    this.socket.onmessage = (evt) => {
      try {
        let packet
        if (typeof evt.data === "string") {
          packet = JSON.parse(evt.data)
        } else if (evt.data instanceof ArrayBuffer) {
          // 二进制数据需要自动检测并解码
          packet = this.utils?.parse ? this.utils.parse(evt.data, "auto") : evt.data
        } else if (evt.data instanceof Blob) {
          // 处理Blob数据
          // 收到Blob数据
          evt.data.arrayBuffer().then(buffer => {
            try {
              packet = this.utils?.parse ? this.utils.parse(buffer, "auto") : buffer
              // Blob解析完成

              // 处理消息体解码（ProtoMsg会自动解码）
              if (packet instanceof Object && packet.rawData !== undefined) {
                if (shouldLog('websocket')) console.log('✅ ProtoMsg Blob消息，使用rawData:', packet.rawData)
              } else if (packet.body && this.shouldDecodeBody(packet.body)) {
                try {
                  if (this.utils && this.utils.bon && this.utils.bon.decode) {
                    // 转换body数据为Uint8Array
                    const bodyBytes = this.convertToUint8Array(packet.body)
                    if (bodyBytes) {
                      const decodedBody = this.utils.bon.decode(bodyBytes)
                      if (shouldLog('websocket')) console.log('🔓 BON Blob解码成功:', packet.cmd, decodedBody)
                      // 不修改packet.body，而是创建一个新的属性存储解码后的数据
                      packet.decodedBody = decodedBody
                    }
                  } else {
                    if (shouldLog('websocket')) console.warn('⚠️ BON解码器不可用 (Blob)')
                  }
                } catch (error) {
                  if (shouldLog('websocket')) console.error('❌ BON Blob消息体解码失败:', error.message, packet.cmd)
                }
              }

              if (this.showMsg) {
                // 收到Blob消息
              }

              // 回调处理
              if (this.messageListener) {
                this.messageListener(packet)
              }

              // Promise 响应处理
              this._handlePromiseResponse(packet)
              
              // 🆕 v3.13.5: 重置空闲计时器
              this._resetIdleTimeout()

            } catch (error) {
              if (shouldLog('websocket')) console.error('Blob解析失败:', error.message)
            }
          })
          return // 异步处理，直接返回
        } else {
          if (shouldLog('websocket')) console.warn('⚠️ 未知数据类型:', typeof evt.data, evt.data)
          packet = evt.data
        }

        if (this.showMsg) {
          if (shouldLog('websocket')) console.log(`📨 收到消息:`, packet)
        }

        // 处理消息体解码（ProtoMsg会自动解码）
        if (packet instanceof Object && packet.rawData !== undefined) {
          if (shouldLog('websocket')) console.log('✅ ProtoMsg消息，使用rawData:', packet.rawData)
        } else {
          // 处理可能存在_raw包装的情况
          const actualPacket = packet._raw || packet

          if (actualPacket.body && this.shouldDecodeBody(actualPacket.body)) {
            try {
              if (this.utils && this.utils.bon && this.utils.bon.decode) {
                // 转换body数据为Uint8Array
                const bodyBytes = this.convertToUint8Array(actualPacket.body)
                if (bodyBytes) {
                  const decodedBody = this.utils.bon.decode(bodyBytes)
                  if (shouldLog('websocket')) console.log('🔓 BON解码成功:', actualPacket.cmd || packet.cmd, decodedBody)
                  // 将解码后的数据存储到原始packet中
                  packet.decodedBody = decodedBody
                  // 如果有_raw结构，也存储到_raw中
                  if (packet._raw) {
                    packet._raw.decodedBody = decodedBody
                  }
                }
              } else {
                if (shouldLog('websocket')) console.warn('⚠️ BON解码器不可用')
              }
            } catch (error) {
              if (shouldLog('websocket')) console.error('❌ BON消息体解码失败:', error.message, actualPacket.cmd || packet.cmd)
            }
          }
        }

        // 回调处理
        if (this.messageListener) {
          this.messageListener(packet)
        }

        // Promise 响应处理
        this._handlePromiseResponse(packet)
        
        // 🆕 v3.13.5: 重置空闲计时器
        this._resetIdleTimeout()

      } catch (error) {
        if (shouldLog('websocket')) console.error(`消息处理失败:`, error.message)
      }
    }

    this.socket.onclose = (evt) => {
      if (shouldLog('websocket')) console.log(`🔌 WebSocket 连接关闭:`, evt.code, evt.reason)
      if (shouldLog('websocket')) console.log(`🔍 关闭详情:`, {
        code: evt.code,
        reason: evt.reason || '未提供原因',
        wasClean: evt.wasClean,
        timestamp: new Date().toISOString()
      })
      this.connected = false
      
      // 🆕 v3.13.5: 清理所有待处理的Promise，避免内存泄漏
      this._rejectAllPendingPromises('连接已关闭')
      
      this._clearTimers()
      if (this.onDisconnect) this.onDisconnect(evt)
    }

    this.socket.onerror = (error) => {
      if (shouldLog('websocket')) console.error(`❌ WebSocket 错误:`, error)
      this.connected = false
      
      // 🆕 v3.13.5: 清理所有待处理的Promise，避免内存泄漏
      this._rejectAllPendingPromises('连接错误: ' + (error.message || '未知错误'))
      
      this._clearTimers()
      if (this.onError) this.onError(error)
    }
  }

  /** 注册消息回调 */
  setMessageListener(fn) {
    this.messageListener = fn
  }

  /** 控制台消息开关 */
  setShowMsg(val) {
    this.showMsg = !!val
  }

  /** 判断是否需要解码body */
  shouldDecodeBody(body) {
    if (!body) return false

    // Uint8Array或Array格式
    if (body instanceof Uint8Array || Array.isArray(body)) {
      return true
    }

    // 对象格式的数字数组（从图片中看到的格式）
    if (typeof body === 'object' && body.constructor === Object) {
      // 检查是否是数字键的对象（例如 {"0": 8, "1": 2, ...}）
      const keys = Object.keys(body)
      return keys.length > 0 && keys.every(key => !isNaN(parseInt(key)))
    }

    return false
  }

  /** 转换body为Uint8Array */
  convertToUint8Array(body) {
    if (!body) return null

    if (body instanceof Uint8Array) {
      return body
    }

    if (Array.isArray(body)) {
      return new Uint8Array(body)
    }

    // 对象格式的数字数组转换为Uint8Array
    if (typeof body === 'object' && body.constructor === Object) {
      const keys = Object.keys(body).map(k => parseInt(k)).sort((a, b) => a - b)
      if (keys.length > 0) {
        const maxIndex = Math.max(...keys)
        const arr = new Array(maxIndex + 1).fill(0)
        for (const [key, value] of Object.entries(body)) {
          const index = parseInt(key)
          if (!isNaN(index) && typeof value === 'number') {
            arr[index] = value
          }
        }
        if (shouldLog('websocket')) console.log('🔄 转换对象格式body为Uint8Array:', arr.length, 'bytes')
        return new Uint8Array(arr)
      }
    }

    return null
  }

  /** 重连 */
  reconnect() {
    this.disconnect()
    setTimeout(() => this.init(), 0)
  }

  /** 断开连接 */
  disconnect() {
    if (this.socket) {
      this.socket.close()
      this.socket = null
    }
    this.connected = false
    this._clearTimers()
  }

  /** 发送消息 */
  send(cmd, params = {}, options = {}) {
    if (!this.connected) {
      if (shouldLog('websocket')) console.warn(`⚠️ WebSocket 未连接，消息已入队: ${cmd}`)
      if (!this.dialogStatus) {
        this.dialogStatus = true
        this.reconnect()
        setTimeout(() => { this.dialogStatus = false }, 0)
      }
    }

    const task = {
      cmd,
      params,
      respKey: options.respKey || cmd,
      sleep: options.sleep || 0,
      onSent: options.onSent
    }

    this.sendQueue.push(task)
    return task
  }

  /** Promise 版发送 */
  sendWithPromise(cmd, params = {}, timeoutMs = 5000) {
    return new Promise((resolve, reject) => {
      if (!this.connected && !this.socket) {
        return reject(new Error("WebSocket 连接已关闭"))
      }

      // 生成唯一的请求ID
      const requestId = `${cmd}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`

      // 设置 Promise 状态
      this.promises[requestId] = { resolve, reject, originalCmd: cmd }

      // 超时处理
      const timer = setTimeout(() => {
        delete this.promises[requestId]
        reject(new Error(`请求超时: ${cmd} (${timeoutMs}ms)`))
      }, timeoutMs)

      // 发送消息
      this.send(cmd, params, {
        respKey: requestId,
        onSent: () => {
          // 消息发送成功后，不要清除超时器，让它继续等待响应
          // 只有在收到响应或超时时才清除
        }
      })
    })
  }

  /** 发送心跳 */
  sendHeartbeat() {
    if (shouldLog('heartbeat')) {
      console.log('💓 发送心跳消息')
    }
    this.send("heart_beat", {}, { respKey: "_sys/ack" })
  }

  /** 获取角色信息 */
  getRoleInfo(params = {}) {
    return this.sendWithPromise("role_getroleinfo", params)
  }

  /** 获取数据版本 */
  getDataBundleVersion(params = {}) {
    return this.sendWithPromise("system_getdatabundlever", params)
  }

  /** 签到 */
  signIn() {
    return this.sendWithPromise("system_signinreward")
  }

  /** 领取日常任务奖励 */
  claimDailyReward(rewardId = 0) {
    return this.sendWithPromise("task_claimdailyreward", { rewardId })
  }

  /** =============== 内部方法 =============== */

  /** 设置心跳 */
  _setupHeartbeat() {
    // 延迟3秒后开始发送第一个心跳，避免连接刚建立就发送
    setTimeout(() => {
      if (this.connected && this.socket?.readyState === WebSocket.OPEN) {
        if (shouldLog('heartbeat')) console.log('💓 开始发送首次心跳')
        this.sendHeartbeat()
      }
    }, 0)

    // 设置定期心跳
    this.heartbeatTimer = setInterval(() => {
      if (this.connected && this.socket?.readyState === WebSocket.OPEN) {
        this.sendHeartbeat()
      } else {
        if (shouldLog('heartbeat')) console.log('⚠️ 心跳检查失败: 连接状态异常')
      }
    }, this.heartbeatInterval)
  }

  /** 队列处理循环 */
  _processQueueLoop() {
    if (this.sendQueueTimer) clearInterval(this.sendQueueTimer)

    this.sendQueueTimer = setInterval(async () => {
      if (!this.sendQueue.length) return
      if (!this.connected || this.socket?.readyState !== WebSocket.OPEN) return

      const task = this.sendQueue.shift()
      if (!task) return

      try {
        // 构建报文
        const raw = this.registry.build(task.cmd, this.ack, this.seq, task.params)
            if (task.cmd !== "heart_beat") this.seq++

        // 编码并发送
        const bin = this.registry.encodePacket(raw)
        this.socket?.send(bin)
        
        // 🆕 v3.13.5: 重置空闲计时器
        this._resetIdleTimeout()

        // 🆕 根据日志配置决定是否输出
        const isHeartbeat = task.cmd === "heart_beat"
        const shouldShowHeartbeat = isHeartbeat && shouldLog('heartbeat')
        const shouldShowWebSocket = !isHeartbeat && shouldLog('websocket')
        
        if (this.showMsg || shouldShowHeartbeat || shouldShowWebSocket) {
          if (shouldLog('websocket')) console.log(`📤 发送消息: ${task.cmd}`, task.params)
          if (this.showMsg) {
            if (shouldLog('websocket')) console.log(`🔐 原始数据:`, raw)
            if (shouldLog('websocket')) console.log(`🚀 编码后数据:`, bin)
            if (shouldLog('websocket')) console.log(`🔧 编码类型:`, typeof bin, bin instanceof Uint8Array ? '✅ Uint8Array (加密)' : '❌ String (明文)')
            if (bin instanceof Uint8Array && bin.length > 0) {
              if (shouldLog('websocket')) console.log(`🎯 加密验证: 前8字节 [${Array.from(bin.slice(0, 8)).join(', ')}]`)
            }
          }
        }

        // 触发发送回调
        if (task.onSent) {
          try {
            task.onSent(task.respKey, task.cmd)
          } catch (error) {
            if (shouldLog('websocket')) console.warn('发送回调执行失败:', error)
          }
        }

        // 可选延时
        if (task.sleep) await sleep(task.sleep)

      } catch (error) {
        if (shouldLog('websocket')) console.error(`❌ 发送消息失败: ${task.cmd}`, error)
      }
    }, 0)
  }

  /** 处理 Promise 响应 */
  _handlePromiseResponse(packet) {
    const cmd = packet.cmd
    
    // 处理没有 cmd 字段但有 resp 字段的响应（通常是错误响应）
    if (!cmd) {
      const resp = packet._raw?.resp || packet.resp
      if (resp !== undefined) {
        // 通过 resp（对应发送的 seq）查找等待的 Promise
        for (const [requestId, promiseData] of Object.entries(this.promises)) {
          // 检查是否匹配（这里需要通过 seq 匹配，但我们没有存储 seq 映射）
          // 由于我们无法直接匹配，先尝试匹配最近的相同命令的 Promise
          if (requestId.startsWith(promiseData.originalCmd + '_')) {
            delete this.promises[requestId]
            
            // 获取错误信息，优先从packet._raw中获取
            const code = packet._raw?.code !== undefined ? packet._raw?.code : packet.code
            const hint = packet._raw?.hint !== undefined ? packet._raw?.hint : packet.hint
            
            // 错误响应
            if (code && code !== 0) {
              promiseData.reject(new Error(`服务器错误: ${code} - ${hint || '未知错误'}`))
            } else {
              // 没有错误码但也没有 cmd，可能是空响应
              promiseData.resolve(packet._raw || packet)
            }
            return
          }
        }
      }
      return
    }

    // 命令到响应的映射 - 处理响应命令与原始命令不匹配的情况
    const responseToCommandMap = {
      // 1:1 响应映射（优先级高）
      'studyresp':'study_startgame',
      'role_getroleinforesp': 'role_getroleinfo',
      'activity_getresp': 'activity_get',  // 月度活动
      'monthlyactivity_receiverewardresp': 'monthlyactivity_receivereward',  // 月度活动领奖
      'fishing_fishresp': 'fishing_fish',  // 钓鱼
      'arena_matchopponentresp': 'arena_matchopponent',  // 竞技场匹配对手
      'arena_battleresp': 'arena_battle',  // 竞技场战斗
      'legionwar_getdetailsresp': 'legionwar_getdetails',  // 军团战详情
      'hero_recruitresp': 'hero_recruit',
      'hero_upgradestarresp': 'hero_heroupgradestar',
      'book_upgraderesp': 'book_upgrade',
      'book_claimpointrewardresp': 'book_claimpointreward',
      'friend_batchresp': 'friend_batch',
      'system_claimhanguprewardresp': 'system_claimhangupreward',
      'item_openboxresp': 'item_openbox',
      'bottlehelper_claimresp': 'bottlehelper_claim',
      'bottlehelper_startresp': 'bottlehelper_start',
      'bottlehelper_stopresp': 'bottlehelper_stop',
      'legion_signinresp': 'legion_signin',
      'fight_startbossresp': 'fight_startboss',
      'fight_startlegionbossresp': 'fight_startlegionboss',
      'fight_startareaarenaresp': 'fight_startareaarena',
      'arena_startarearesp': 'arena_startarea',
      'arena_getareatargetresp': 'arena_getareatarget',
      'presetteam_saveteamresp': 'presetteam_saveteam',
      'presetteam_changeteamresp': 'presetteam_changeteam',
      'presetteam_getinforesp': 'presetteam_getinfo',
      'mail_claimallattachmentresp': 'mail_claimallattachment',
      'store_buyresp': 'store_purchase',
      'system_getdatabundleverresp': 'system_getdatabundlever',
      'tower_claimrewardresp': 'tower_claimreward',
      'fight_starttowerresp': 'fight_starttower',

      // 赛车相关响应映射
      'car_getrolecarresp': 'car_getrolecar',
      'car_refreshresp': 'car_refresh',
      'car_claimresp': 'car_claim',
      'car_sendresp': 'car_send',

      // 特殊响应映射 - 有些命令有独立响应，有些用同步响应
      'task_claimdailyrewardresp': 'task_claimdailyreward',
      'task_claimweekrewardresp': 'task_claimweekreward',

      // 同步响应映射（优先级低）
      'syncresp': ['system_mysharecallback', 'task_claimdailypoint'],
      'syncrewardresp': ['system_buygold', 'discount_claimreward', 'card_claimreward',
                        'artifact_lottery', 'genie_sweep', 'genie_buysweep','system_signinreward']
    }

    // 获取原始命令名（支持一对一和一对多映射）
    let originalCmds = responseToCommandMap[cmd]
    if (!originalCmds) {
      originalCmds = [cmd] // 如果没有映射，使用响应命令本身
    } else if (typeof originalCmds === 'string') {
      originalCmds = [originalCmds] // 转换为数组
    }

    // 查找对应的 Promise - 遍历所有等待中的 Promise
    for (const [requestId, promiseData] of Object.entries(this.promises)) {
      // 检查 Promise 是否匹配当前响应的任一原始命令
      if (originalCmds.includes(promiseData.originalCmd)) {
        delete this.promises[requestId]

        // 获取响应数据，优先使用 rawData（ProtoMsg 自动解码），然后 decodedBody（手动解码），最后 body
        const responseBody = packet.rawData !== undefined ? packet.rawData :
                           packet.decodedBody !== undefined ? packet.decodedBody :
                           packet.body

        // 检查错误码，优先使用 _raw 中的错误码，然后是 packet 中的错误码
        const code = packet._raw?.code !== undefined ? packet._raw?.code : packet.code
        const hint = packet._raw?.hint !== undefined ? packet._raw?.hint : packet.hint

        // 简单判断：只有code为0或undefined时才认为成功
        if (code === 0 || code === undefined) {
          promiseData.resolve(responseBody || packet)
        } else {
          promiseData.reject(new Error(`服务器错误: ${code} - ${hint || '未知错误'}`))
        }
        break
      }
    }
  }

  /** 清理定时器 */
  _clearTimers() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
    if (this.sendQueueTimer) {
      clearInterval(this.sendQueueTimer)
      this.sendQueueTimer = null
    }
    // 🆕 v3.13.5: 清理空闲超时定时器
    this._stopIdleTimeout()
  }
  
  // ==================== 🆕 v3.13.5: 空闲超时机制 ====================
  
  /**
   * 启动空闲超时检测
   */
  _startIdleTimeout() {
    if (this.idleTimeout <= 0) return // 禁用空闲超时
    
    this.lastActivityTime = Date.now()
    this._resetIdleTimeout()
    
    if (shouldLog('websocket')) {
      console.log(`⏰ [空闲检测] 已启动，超时时间: ${this.idleTimeout / 1000}秒`)
    }
  }
  
  /**
   * 重置空闲超时计时器
   */
  _resetIdleTimeout() {
    if (this.idleTimeout <= 0) return // 禁用空闲超时
    
    // 更新最后活动时间
    this.lastActivityTime = Date.now()
    
    // 清除旧的定时器
    if (this.idleTimer) {
      clearTimeout(this.idleTimer)
    }
    
    // 设置新的定时器
    this.idleTimer = setTimeout(() => {
      const idleTime = Date.now() - this.lastActivityTime
      
      if (shouldLog('websocket')) {
        console.log(`⏰ [空闲检测] 连接空闲 ${Math.floor(idleTime / 1000)}秒，自动断开`)
      }
      
      // 空闲超时，断开连接
      this.disconnect()
    }, this.idleTimeout)
  }
  
  /**
   * 停止空闲超时检测
   */
  _stopIdleTimeout() {
    if (this.idleTimer) {
      clearTimeout(this.idleTimer)
      this.idleTimer = null
    }
  }
  
  // ==================== 结束：空闲超时机制 ====================
  
  // ==================== 🆕 v3.13.5: Promise清理机制 ====================
  
  /**
   * 拒绝所有待处理的Promise（避免内存泄漏）
   * @param {string} reason - 拒绝原因
   */
  _rejectAllPendingPromises(reason) {
    const pendingCount = Object.keys(this.promises).length
    
    if (pendingCount > 0) {
      if (shouldLog('websocket')) {
        console.log(`🧹 [Promise清理] 清理 ${pendingCount} 个待处理的Promise`)
      }
      
      // 拒绝所有待处理的Promise
      Object.entries(this.promises).forEach(([requestId, promiseData]) => {
        const cmd = promiseData.originalCmd || promiseData.cmd || 'unknown'
        if (shouldLog('websocket')) {
          console.log(`🧹 [Promise清理] 拒绝: ${cmd} (${requestId})`)
        }
        promiseData.reject(new Error(`${reason} (命令: ${cmd})`))
      })
      
      // 清空promises对象
      this.promises = Object.create(null)
    }
  }
  
  // ==================== 结束：Promise清理机制 ====================
}

/** 默认导出 */
export default XyzwWebSocketClient
