// 聊天页面逻辑
const app = getApp();
const mediaPicker = require('../../../utils/media-picker.js');
const { initPageSystemInfo } = require('../../../utils/system-info-modern.js');
const imageCacheManager = require('../../../utils/image-cache-manager.js');
const nimConversationManager = require('../../../utils/nim-conversation-manager.js');
const nimUserManager = require('../../../utils/nim-user-manager.js');
const friendAPI = require('../../../utils/friend-api.js');

Page({
  data: {
    // 聊天基本信息
    conversationId: '',
    targetId: '',
    chatName: '',
    chatType: 0, // 0: 单聊, 1: 群聊
    isOnline: false,
    isNewConversation: false, // 是否为新会话

    // 消息数据
    messages: [],
    hasMore: true,
    lastMessageTime: 0,
    unreadCount: 0,
    loadingMessages: false, // 🔥 添加加载状态

    // 输入相关
    inputType: 'text', // 'text' 或 'voice'
    inputText: '',
    inputFocus: false,
    recording: false,
    voiceCancel: false, // 🔥 语音录制取消标识
    isComposing: false, // 中文输入法状态

    // 面板状态
    showEmojiPanel: false,
    showMorePanel: false,
    showVoiceRecorder: false,

    // 滚动相关
    scrollTop: 0,
    scrollIntoView: '',
    showScrollToBottom: false, // 🔥 控制滚动到底部按钮显示

    // 常用表情
    commonEmojis: [
      '😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃',
      '😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙',
      '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🤫', '🤔',
      '🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', '😬', '🤥',
      '😌', '😔', '😪', '🤤', '😴', '😷', '🤒', '🤕', '🤢', '🤮',
      '🤧', '🥵', '🥶', '🥴', '😵', '🤯', '🤠', '🥳', '🥸', '😎',
      '🤓', '🧐', '😕', '😟', '🙁', '☹️', '😮', '😯', '😲', '😳',
      '🥺', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖',
      '😣', '😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠', '🤬',
      '👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞',
      '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍',
      '👎', '👊', '✊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝'
    ],

    // 用户信息
    userInfo: null,
    userAvatar: '',
    targetUserInfo: null, // 对方用户信息
    targetAvatar: '', // 对方头像

    // 系统适配信息
    systemInfo: {},
    statusBarHeight: 0,
    menuButtonHeight: 0,
    menuButtonTop: 0,
    navBarHeight: 0,
    windowHeight: 0,
    safeAreaBottom: 0,
    keyboardHeight: 0, // 键盘高度
    inputAreaHeight: 0, // 输入框区域高度
    emojiPanelHeight: 0, // 表情面板高度

    // 加载状态
    isLoading: false,
  },

  // 🔍 判断内容是否像图片（URL/Base64/JSON中包含图片URL）
  _looksLikeImageContent(content) {
    try {
      if (!content) return false;
      const isImgUrl = (u) => {
        if (typeof u !== 'string') return false;
        if (/^data:image\//i.test(u)) return true;
        if (!/^https?:\/\//i.test(u)) return false;
        return /\.(png|jpe?g|gif|webp|bmp|svg)(\?.*)?$/i.test(u);
      };
      if (typeof content === 'string') {
        const s = content.trim();
        if (isImgUrl(s)) return true;
        if (s.startsWith('{') && s.endsWith('}')) {
          try {
            const obj = JSON.parse(s);
            const u = obj?.file_url || obj?.url;
            return isImgUrl(u || '');
          } catch (_) { return false; }
        }
        return false;
      }
      if (typeof content === 'object') {
        const u = content.file_url || content.url;
        return isImgUrl(u || '');
      }
      return false;
    } catch (_) {
      return false;
    }
  },

  // 🔥 将 NIM SDK V2 的数字类型枚举转换为 WXML 中使用的字符串类型
  // V2NIMMessageType: 0=文本, 1=图片, 2=语音, 3=视频, 4=位置, 5=通知, 6=文件, 10=提示, 100=自定义
  convertMessageTypeToString(messageType) {
    const typeMap = {
      0: 'text',      // 文本
      1: 'image',     // 图片
      2: 'audio',     // 语音（WXML中也兼容'voice'）
      3: 'video',     // 视频
      4: 'location',  // 位置
      5: 'notification', // 通知
      6: 'file',      // 文件
      10: 'tip',      // 提示
      100: 'custom'   // 自定义
    };
    return typeMap[messageType] || 'text';
  },


  /**
   * 点击自己头像 -> 跳转个人资料页
   */
  onSelfAvatarTap(e) {
    try {
      const userId = this.data.userInfo?.user?.customId;
      if (!userId) return;
      wx.navigateTo({
        url: `/subpackages/profile/profile/profile?userId=${userId}`
      });
    } catch (err) {
      console.error('进入个人资料失败', err);
    }
  },

  /**
   * 点击对方头像 -> 跳转好友详情页（单聊）或成员资料（群聊可后续拓展）
   */
  async onPeerAvatarTap(e) {
    try {
      const senderId = e.currentTarget.dataset.senderId;
      if (!senderId) {
        console.warn('⚠️ senderId 为空，无法跳转');
        return;
      }
      // // 如果是自己就直接走自己的逻辑
      // const selfId = this.data.userInfo?.user?.customId;
      // if (senderId === selfId) {
      //   this.onSelfAvatarTap();
      //   return;
      // }

      wx.showLoading({ title: '加载中...', mask: true });
      // 调用好友详情接口判断关系
      let res = await friendAPI.getFriendDetail(senderId)
      wx.hideLoading();
      if (res?.code == 0 || res?.code == 200) {
        console.log('√ 是好友，跳转好友详情页');
        wx.navigateTo({
          url: `/subpackages/social/friend-detail/friend-detail?customId=${senderId}`
        });
      } else {
        console.log('！ 非好友，跳转陌生人预览页');
        wx.navigateTo({
          url: `/subpackages/social/user-preview/user-preview?customId=${senderId}`
        });
      }
    } catch (err) {
      console.error('× 查询好友关系失败:', err);
      wx.hideLoading();
      const senderId = e.currentTarget.dataset.senderId;
      if (senderId) {
        wx.navigateTo({
          url: `/subpackages/social/user-preview/user-preview?customId=${senderId}`
        });
      }
    }
  },

  async onLoad(options) {

    // 显示加载状态
    this.setData({ isLoading: true });

    // 初始化系统信息（同步，立即完成）
    this.initSystemInfo();

    // 检查 NIM 状态
    const appInstance = getApp();
    this.nim = appInstance?.globalData?.nim || null;
    this._nimOldestMessage = null;

    if (!this.nim || !this.nim.V2NIMMessageService || !this.nim.V2NIMConversationService) {
      console.error('❌ NIM 服务不可用');
      wx.showToast({
        title: 'NIM服务不可用',
        icon: 'none'
      });
      return;
    }

    console.log('✅ NIM消息服务可用，启用NIM模式加载消息');

    // 并行初始化用户与聊天信息
    const userInfoPromise = this.initUserInfo();
    const chatInfoPromise = this.initChatInfo(options);

    await userInfoPromise;

    await chatInfoPromise;

    // 隐藏加载状态
    this.setData({ isLoading: false });

    // 注册 NIM 监听器
    this.attachNIMListeners();
  },

  onShow: function () {
    console.log('聊天页面显示');

    // 🔥 检查并重连 NIM（防止从后台恢复后 NIM 断开）
    this.checkAndReconnectNim();

    // 进入聊天页面时自动标记所有消息已读
    this.markAllMessagesRead();
  },

  onReady() {
    // 页面已就绪，获取输入框区域的实际高度
    this.updateInputAreaHeight();
  },

  // 更新输入框区域高度
  updateInputAreaHeight() {
    wx.createSelectorQuery()
      .select('.input-area')
      .boundingClientRect(rect => {
        if (rect && rect.height) {
          console.log('📏 输入框区域高度:', rect.height);
          this.setData({
            inputAreaHeight: rect.height
          });
        }
      })
      .exec();
  },

  onHide: function () {
    // 页面隐藏时可以保持会话状态
  },

  onUnload: function () {
    // 🔥 清理录音相关定时器
    if (this.voiceStartTimer) {
      clearTimeout(this.voiceStartTimer);
      this.voiceStartTimer = null;
    }

    // 页面卸载时清理 NIM 监听器
    this.detachNIMListeners();
  },

  // 注册 NIM 事件监听器
  attachNIMListeners() {
    console.log('📡 注册 NIM 事件监听器');

    const nim = getApp().globalData.nim;
    if (!nim || !nim.V2NIMMessageService) {
      console.warn('⚠️ NIM SDK 不可用，无法注册监听器');
      return;
    }

    // 🔥 使用 bind(this) 确保回调中的 this 上下文正确
    // 监听新消息
    this._nimMessageListener = this.handleNewNIMMessage.bind(this);

    // 🔥 监听消息撤回通知
    this._nimMessageRevokeListener = this.handleMessageRevokeNotifications.bind(this);

    // 注册监听器（根据NIM SDK文档调整）
    try {
      nim.V2NIMMessageService.on('onReceiveMessages', this._nimMessageListener);
      nim.V2NIMMessageService.on('onMessageRevokeNotifications', this._nimMessageRevokeListener);
      console.log('✅ NIM 监听器注册成功');
    } catch (error) {
      console.error('❌ 注册 NIM 监听器失败:', error);
    }
  },

  // 清理 NIM 事件监听器
  detachNIMListeners() {
    console.log('🔌 清理 NIM 事件监听器');

    const nim = getApp().globalData.nim;
    if (!nim || !nim.V2NIMMessageService) {
      return;
    }

    try {
      if (this._nimMessageListener) {
        nim.V2NIMMessageService.off('onReceiveMessages', this._nimMessageListener);
        this._nimMessageListener = null;
      }
      if (this._nimMessageRevokeListener) {
        nim.V2NIMMessageService.off('onMessageRevokeNotifications', this._nimMessageRevokeListener);
        this._nimMessageRevokeListener = null;
      }
      console.log('✅ NIM 监听器清理成功');
    } catch (error) {
      console.error('❌ 清理 NIM 监听器失败:', error);
    }
  },

  // 🔥 检查并重连 NIM（页面级别）
  async checkAndReconnectNim() {
    try {
      const app = getApp();

      // 触发全局 NIM 重连检查
      if (app && typeof app.checkAndReconnectNim === 'function') {
        await app.checkAndReconnectNim();
      }

      // 检查 NIM 实例是否可用
      const nim = app.globalData.nim;
      if (!nim || !nim.V2NIMMessageService) {
        console.warn('⚠️ NIM 实例不可用');
        return;
      }

      // 重新注册事件监听器（确保监听器有效）
      if (!this._nimMessageListener || !this._nimMessageRevokeListener) {
        console.log('🔄 重新注册 NIM 事件监听器');
        this.detachNIMListeners(); // 先清理旧的
        this.attachNIMListeners();  // 重新注册
      }

      // 检查会话管理器状态
      if (!nimConversationManager.getInitStatus()) {
        console.log('🔄 重新初始化会话管理器');
        nimConversationManager.init(nim);
      }

      console.log('✅ 聊天页面 NIM 状态检查完成');
    } catch (error) {
      console.error('❌ 聊天页面 NIM 重连检查失败:', error);
    }
  },

  // 处理 NIM 新消息
  async handleNewNIMMessage(messages) {
    try {
      console.log('📨 处理 NIM 新消息，数量:', messages?.length || 0);

      // 🔥 NIM SDK V2 的 onReceiveMessages 回调参数是消息数组
      if (!Array.isArray(messages) || messages.length === 0) {
        console.warn('⚠️ 收到空消息数组或非数组数据');
        return;
      }

      // 过滤出属于当前会话的消息
      const currentConversationMessages = messages.filter(
        msg => msg.conversationId === this.data.conversationId
      );

      if (currentConversationMessages.length === 0) {
        console.log('⚠️ 没有属于当前会话的消息，跳过');
        return;
      }

      console.log('✅ 收到当前会话的新消息:', currentConversationMessages.length, '条');

      // 格式化所有新消息
      const formattedMessages = await Promise.all(
        currentConversationMessages.map(msg => this.formatMessage(msg))
      );

      // 去重处理 - 防止重复显示已存在的消息
      const existingIds = new Set(this.data.messages.map(msg => msg.messageId));
      const uniqueNewMessages = formattedMessages.filter(
        msg => !existingIds.has(msg.messageId)
      );

      if (uniqueNewMessages.length === 0) {
        console.log('⚠️ 所有新消息均已存在,跳过重复添加');
        return;
      }

      console.log('✅ 去重后新消息数量:', uniqueNewMessages.length);

      // 添加到消息列表
      const updatedMessages = this.data.messages.concat(uniqueNewMessages);
      this.setMessagesWithDate(updatedMessages);

      // 滚动到底部
      setTimeout(() => this.scrollToBottom(), 100);

      // 标记已读
      await nimConversationManager.markConversationRead(this.data.conversationId);

      console.log('✅ 新消息处理完成');
    } catch (error) {
      console.error('❌ 处理新消息失败:', error);
    }
  },

  // 处理消息撤回通知
  handleMessageRevokeNotifications(revokeNotifications) {
    try {
      if (!Array.isArray(revokeNotifications) || revokeNotifications.length === 0) {
        return;
      }

      // 过滤出属于当前会话的撤回通知
      const currentConversationRevokes = revokeNotifications.filter(
        revoke => revoke.messageRefer?.conversationId === this.data.conversationId
      );

      if (currentConversationRevokes.length === 0) {
        return;
      }

      // 更新本地消息状态
      currentConversationRevokes.forEach(revoke => {
        const messageRefer = revoke.messageRefer;
        if (!messageRefer) return;

        const messageServerId = messageRefer.messageServerId;
        const messageClientId = messageRefer.messageClientId;

        // 尝试找到匹配的消息
        let foundMessage = null;

        // 方式1: 通过 messageServerId 查找
        if (messageServerId) {
          foundMessage = this.data.messages.find(m =>
            m.messageId === messageServerId ||
            m._rawMessage?.messageServerId === messageServerId
          );
        }

        // 方式2: 通过 messageClientId 查找
        if (!foundMessage && messageClientId) {
          foundMessage = this.data.messages.find(m =>
            m.messageId === messageClientId ||
            m._rawMessage?.messageClientId === messageClientId
          );
        }

        if (foundMessage) {
          this.updateMessageToRecalled(foundMessage.messageId);
        }
      });
    } catch (error) {
      console.error('处理撤回通知失败:', error);
    }
  },

  // 初始化系统信息
  initSystemInfo() {
    const pageSystemInfo = initPageSystemInfo();

    this.setData({
      systemInfo: pageSystemInfo.systemInfo,
      statusBarHeight: pageSystemInfo.statusBarHeight,
      menuButtonHeight: pageSystemInfo.menuButtonHeight,
      menuButtonTop: pageSystemInfo.menuButtonTop,
      navBarHeight: pageSystemInfo.navBarHeight,
      windowHeight: pageSystemInfo.windowHeight,
      safeAreaBottom: pageSystemInfo.safeAreaBottom,
      keyboardHeight: 0 // 初始化键盘高度
    });
  },

  // 🔥 监听键盘高度变化
  onKeyboardHeightChange(e) {
    const { height, duration } = e.detail;

    const inputAreaHeight = this.data.inputAreaHeight;
    const totalBottom = inputAreaHeight + (height || 0);

    console.log('⌨️ 键盘高度变化:', {
      keyboardHeight: height,
      inputAreaHeight,
      totalBottom,
      duration
    });

    // 直接使用微信提供的键盘高度（单位：px）
    this.setData({
      keyboardHeight: height || 0
    }, () => {
      // 键盘弹出时，滚动到底部，确保最新消息可见
      if (height > 0) {
        this.scrollToBottom(true);
      }
    });
  },

  // 初始化聊天信息
  async initChatInfo(options) {
    const inputConversationId = options.conversationId || '';
    const targetId = options.targetId || '';
    const chatName = decodeURIComponent(options.name || '聊天');
    const chatType = parseInt(options.chatType || 0);
    const incomingTargetAvatar = options.targetAvatar || '';
    const sendMessage = options.sendMessage?.trim() || '';

    // 临时设置基本信息
    this.setData({
      targetId,
      chatName,
      chatType,
      inputText: sendMessage,
      conversationId: inputConversationId,
      targetAvatar: incomingTargetAvatar || this.data.targetAvatar || ''
    });

    // 🔥 动态设置导航栏标题为用户昵称
    wx.setNavigationBarTitle({
      title: chatName
    });

    // 🔥 从 NIM 获取对方用户信息（头像和昵称）
    // 注意：如果 targetId 是会话ID格式（如 "p2p-xxx" 或 "16950956|1|33981539"），需要提取真实用户ID
    if (targetId) {
      let realUserId = targetId;

      // 🔥 如果是 NIM 会话ID格式（包含 | 分隔符），提取真实用户ID
      // 格式：senderId|conversationType|receiverId（如 "16950956|1|33981539"）
      if (targetId.includes('|')) {
        const parts = targetId.split('|');
        if (parts.length >= 3) {
          // 对于单聊（type=1），receiverId 是对方用户ID（parts[2]）
          realUserId = parts[2];
          console.log('🔧 从会话ID提取用户ID:', { conversationId: targetId, userId: realUserId });
        }
      }

      await this.loadTargetUserInfo(realUserId);
    }

    // 🔥 如果没有传入conversationId，先检查会话是否已存在
    if (!inputConversationId) {
      console.log('🔍 未传入会话ID，检查是否已存在该会话');

      if (!targetId) {
        console.error('❌ 缺少 targetId，无法查询会话');
        return;
      }

      // 使用NIM生成会话ID
      const conversationId = this.buildNIMConversationId(targetId, chatType);
      if (!conversationId) {
        console.error('❌ 无法生成会话ID');
        return;
      }

      console.log('🔍 生成的会话ID:', conversationId);

      // 🔥 检查会话是否已存在（从NIM SDK获取会话列表查询）
      try {
        const nim = getApp().globalData.nim;
        if (nim && nim.V2NIMConversationService) {
          // 🔥 获取会话列表并查找目标会话
          console.log('🔍 从NIM SDK查询会话是否存在...');
          const result = await nim.V2NIMConversationService.getConversationList(0, 100);
          const existingConv = result?.conversationList?.find(c => c.conversationId === conversationId);

          if (existingConv) {
            console.log('✅ 会话已存在，使用现有会话:', existingConv);
            this.setData({
              conversationId,
              isNewConversation: false
            });

            // 加载消息
            await this.loadMessages();
            return;
          } else {
            console.log('📝 会话不存在，标记为新会话');
          }
        }
      } catch (error) {
        console.warn('⚠️ 查询会话失败，将作为新会话处理:', error);
      }

      // 🔥 会话不存在，标记为新会话（不加载历史消息）
      console.log('✅ 创建新会话ID:', conversationId);
      this.setData({
        conversationId,
        isNewConversation: true,
        messages: []
      });
      return;
    }

    // 使用传入的conversationId
    this.setData({ conversationId: inputConversationId });

    // 加载消息
    await this.loadMessages();
  },

  // 🔥 验证并使用传入的会话ID
  buildNIMConversationId(targetId, chatType) {
    try {
      if (!this.nim || !this.nim.V2NIMConversationIdUtil) {
        return '';
      }
      if (!targetId) {
        return '';
      }
      if (Number(chatType) === 1) {
        return this.nim.V2NIMConversationIdUtil.teamConversationId(String(targetId));
      }
      return this.nim.V2NIMConversationIdUtil.p2pConversationId(String(targetId));
    } catch (err) {
      console.warn('构造 NIM 会话ID 失败:', err);
      return '';
    }
  },

  // 🔥 从 NIM SDK 获取对方用户信息（包括头像和昵称）
  async loadTargetUserInfo(targetId) {
    try {
      console.log('📡 从 NIM 获取用户信息:', targetId);

      // 🔥 使用 NIM SDK 获取用户信息
      const userInfo = await nimUserManager.getUserInfo(targetId);

      if (userInfo) {
        const targetAvatar = userInfo.avatar || '';
        const targetNickname = userInfo.nickname || '';

        // 🔥 先设置用户信息，头像缓存异步执行
        this.setData({
          targetUserInfo: {
            avatar: targetAvatar,
            nickname: targetNickname
          },
          targetAvatar: targetAvatar, // 先使用原始URL
          chatName: targetNickname || this.data.chatName // 更新聊天名称
        });

        console.log('✅ 用户信息加载成功（从 NIM）:', { targetNickname, targetAvatar });

        // 🔥 异步缓存头像，不阻塞UI
        if (targetAvatar) {
          imageCacheManager.cacheAvatar(targetAvatar).then(cachedAvatar => {
            this.setData({
              targetAvatar: cachedAvatar
            });
          }).catch(error => {
            console.error('❌ 对方头像缓存失败:', error);
          });
        }
      } else {
        console.warn('⚠️ 从 NIM 未获取到用户信息');
      }

    } catch (error) {
      console.error('❌ 从 NIM 获取用户信息异常:', error);
    }
  },

  // 初始化用户信息
  async initUserInfo() {
    // 先尝试从全局获取
    let userInfo = app.globalData.userInfo;

    // 如果全局没有，尝试从本地存储获取
    if (!userInfo || !userInfo.token) {
      try {
        const storedUserInfo = wx.getStorageSync('userInfo');
        if (storedUserInfo && storedUserInfo.token) {
          userInfo = storedUserInfo;
          // 更新全局数据
          app.globalData.userInfo = userInfo;
          app.globalData.isLoggedIn = true;

        }
      } catch (error) {
        console.error('从本地存储获取用户信息失败:', error);
      }
    }

    // 确保有用户信息
    if (userInfo && userInfo.token) {
      // 正确获取用户头像
      const userAvatar = userInfo.user?.avatar || userInfo.avatar || '';


      // 🔥 先设置用户信息，头像缓存异步执行
      this.setData({
        userInfo: userInfo,
        userAvatar: userAvatar // 先使用原始URL
      });

      // 🔥 异步缓存头像，不阻塞UI
      imageCacheManager.cacheAvatar(userAvatar).then(cachedAvatar => {
        this.setData({
          userAvatar: cachedAvatar
        });

      }).catch(error => {
        console.error('❌ 用户头像缓存失败:', error);
      });

    } else {
      console.error('用户信息初始化失败，跳转到登录页');
      wx.reLaunch({
        url: '/pages/login/login'
      });
    }
  },

  // 加载消息 - 使用 NIM SDK
  async loadMessages(isLoadMore = false) {
    try {
      console.log('📨 加载聊天消息 (NIM SDK):', {
        isLoadMore,
        conversationId: this.data.conversationId
      });

      // 检查 conversationId
      if (!this.data.conversationId) {
        console.log('⚠️ 缺少 conversationId，无法加载消息');
        return;
      }

      // 避免重复请求
      if (this.data.loadingMessages) {
        console.log('⚠️ 消息正在加载中，跳过重复请求');
        return;
      }

      // 设置加载状态
      this.setData({ loadingMessages: true });

      // 获取 NIM 实例
      const nim = getApp().globalData.nim;
      if (!nim || !nim.V2NIMMessageService) {
        throw new Error('NIM SDK 未初始化');
      }

      // 使用 NIM SDK 获取历史消息
      const limit = 20;
      const option = {
        conversationId: this.data.conversationId,
        endTime: isLoadMore && this.data.lastMessageTime ? this.data.lastMessageTime : Date.now(),
        limit: limit,
        direction: 0 // 1 表示查询比 endTime 更早的消息
      };

      console.log('📡 查询参数:', option);

      const result = await nim.V2NIMMessageService.getMessageList(option);

      if (!result) {
        console.log('⚠️ NIM SDK 返回空结果');
        this.setData({ loadingMessages: false, hasMore: false });
        return;
      }

      const newMessages = result || [];
      console.log('✅ NIM 消息回调:', newMessages);
      console.log('✅ 从 NIM 获取到消息:', newMessages.length, '条');

      // 格式化消息
      const formattedMessages = await Promise.all(
        newMessages.map(msg => this.formatMessage(msg))
      );

      // 判断是否是首次加载
      const isFirstLoad = this.data.messages.length === 0;

      // 去重处理
      const existingIds = new Set(this.data.messages.map(msg => msg.messageId));
      const uniqueNewMessages = formattedMessages.filter(
        msg => !existingIds.has(msg.messageId)
      );

      // 合并并排序消息
      let sortedMessages;
      if (isFirstLoad) {
        sortedMessages = uniqueNewMessages.sort((a, b) => a.timestamp - b.timestamp);
      } else {
        const allMessages = uniqueNewMessages.concat(this.data.messages);
        sortedMessages = allMessages.sort((a, b) => a.timestamp - b.timestamp);
      }

      // 更新 lastMessageTime（最早消息的时间戳，用于分页）
      const earliestMessage = newMessages.length > 0
        ? newMessages[newMessages.length - 1]
        : null;
      const newLastMessageTime = earliestMessage?.createTime || earliestMessage?.timestamp || 0;

      // 添加日期分隔符
      const annotated = this.recomputeDateDividers(this._filterDeleted(sortedMessages));

      // 更新数据
      this.setData({
        messages: annotated,
        hasMore: newMessages.length === limit,
        lastMessageTime: newLastMessageTime,
        loadingMessages: false
      });

      console.log('✅ 消息加载完成:', {
        totalMessages: sortedMessages.length,
        hasMore: newMessages.length === limit,
        lastMessageTime: newLastMessageTime
      });

      // 首次加载时滚动到底部
      if (isFirstLoad && this.data.messages.length > 0) {
        setTimeout(() => this.scrollToBottom(), 100);
      }
    } catch (error) {
      console.error('❌ 加载消息失败:', error);
      this.setData({
        hasMore: false,
        loadingMessages: false
      });
      wx.showToast({
        title: '加载消息失败',
        icon: 'none'
      });
    }
  },

  // 格式化消息 - 适配接口文档的数据结构，特殊处理图片消息
  async formatMessage(msgData) {
    // 获取当前用户的NIM账号ID（用于判断消息是否为自己发送）
    let currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';

    // 如果从data中获取不到，尝试从全局获取
    if (!currentNimAccountId) {
      const app = getApp();
      const globalUserInfo = app.globalData.userInfo;
      currentNimAccountId = globalUserInfo?.neteaseIMAccid || globalUserInfo?.user?.neteaseIMAccid || '';
    }

    // 如果还是获取不到，尝试从本地存储获取
    if (!currentNimAccountId) {
      try {
        const storedUserInfo = wx.getStorageSync('userInfo');
        currentNimAccountId = storedUserInfo?.neteaseIMAccid || storedUserInfo?.user?.neteaseIMAccid || '';
      } catch (error) {
        console.error('从本地存储获取NIM账号ID失败:', error);
      }
    }

    // 适配 NIM SDK V2 官方字段名
    const senderId = msgData.senderId || msgData.senderAccountId;
    const messageId = msgData.messageClientId || msgData.messageServerId || msgData.id || msgData.messageId;
    const isSelf = senderId === currentNimAccountId;

    // 🔥 直接使用 NIM SDK V2 的 messageType 枚举值（无需转换）
    // V2NIMMessageType: 0=文本, 1=图片, 2=语音, 3=视频, 4=位置, 5=通知, 6=文件, 10=提示, 100=自定义
    let messageType = msgData.messageType !== undefined ? msgData.messageType : msgData.msgType;

    // 🔥 获取消息内容 - NIM SDK V2 使用 text(文本) 或 attachment(附件)
    let _rawContent;
    if (msgData.text !== undefined && msgData.text !== null) {
      _rawContent = msgData.text;
    } else if (msgData.attachment !== undefined && msgData.attachment !== null) {
      _rawContent = msgData.attachment;
    } else if (msgData.content !== undefined && msgData.content !== null) {
      _rawContent = msgData.content;
    } else {
      _rawContent = '';
    }

    // 标记为图片消息(1)但内容不是可用图片 -> 降级为文本(0)
    if (messageType === 1) {
      let hasValidImage = false;
      try {
        if (typeof _rawContent === 'string') {
          const s = _rawContent.trim();
          if (/^https?:\/\//i.test(s) || /^data:image\//i.test(s)) {
            hasValidImage = true;
          } else if (s.startsWith('{') && s.endsWith('}')) {
            const obj = JSON.parse(s);
            const u = obj?.file_url || obj?.url;
            hasValidImage = !!u;
          }
        } else if (typeof _rawContent === 'object' && _rawContent !== null) {
          const u = _rawContent.file_url || _rawContent.url;
          hasValidImage = !!u;
        }
      } catch (_) { hasValidImage = false; }
      if (!hasValidImage) {
        console.log('🔧 检测到图片类型但无有效URL，降级为文本');
        messageType = 0;  // 降级为文本
      }
    }
    // 标记为文本(0)但内容像图片 -> 升级为图片(1)
    if (messageType === 0 && this._looksLikeImageContent(_rawContent)) {
      console.log('🖼️ 文本消息内容识别为图片，自动升级为图片类型');
      messageType = 1;
    }

    // 🔥 撤回态识别（历史记录/服务端数据）
    // 注意：撤回状态应该通过 msgData.isRecalled 字段判断，不应该依赖 sendingState
    // sendingState 枚举值: 0=未知, 1=成功, 2=失败, 3=发送中
    const recalledFromServer = !!msgData.isRecalled;  // 只通过 isRecalled 字段判断是否撤回

    // 🔥 特殊处理媒体消息内容
    let processedContent = _rawContent;
    if (messageType === 1) {  // 图片
      processedContent = await this.parseImageContent(_rawContent);
      console.log('🖼️ 图片消息处理结果:', processedContent);
    } else if (messageType === 2) {  // 语音
      processedContent = this.parseVoiceContent(_rawContent);
      console.log('🎤 语音消息处理结果:', processedContent);
    } else if (messageType === 6) {  // 文件
      processedContent = this.parseFileContent(_rawContent);
      console.log('📄 文件消息处理结果:', processedContent);
    } else if (messageType === 4) {  // 位置
      processedContent = this.parseLocationContent(_rawContent);
      console.log('📍 位置消息处理结果:', processedContent);
    } else if (messageType === 3) {  // 视频
      processedContent = this.parseVideoContent(_rawContent);
      console.log('🎬 视频消息处理结果:', processedContent);
    } else if (messageType === 0) {  // 文本
      processedContent = msgData.text || _rawContent;
    }

    // 🔥 统一时间戳字段 - NIM SDK V2 使用 createTime
    let timestamp = 0;
    try {
      // 优先使用 createTime，兼容其他时间字段
      if (typeof msgData.createTime === 'number') {
        timestamp = msgData.createTime;
      } else if (typeof msgData.sendTime === 'number') {
        timestamp = msgData.sendTime;
      } else if (typeof msgData.sendTime === 'string') {
        const t = Date.parse(msgData.sendTime);
        timestamp = isNaN(t) ? Date.now() : t;
      } else {
        timestamp = Number(msgData.timestamp || Date.now());
      }
    } catch (_) {
      timestamp = Date.now();
    }

    // 🔥 获取发送者头像（已通过 NIM getUserInfo 获取并缓存）
    const senderAvatar = isSelf ? (this.data.userAvatar || '') : (this.data.targetAvatar || '');

    // 🔥 处理消息发送状态
    let deliveryStatus;
    if (isSelf) {
      // 根据官方文档的正确枚举值处理
      if (msgData.sendingState === 3) {
        deliveryStatus = 3; // 发送中 (V2NIM_MESSAGE_SENDING_STATE_SENDING)
      } else if (msgData.sendingState === 2) {
        deliveryStatus = 2; // 发送失败 (V2NIM_MESSAGE_SENDING_STATE_FAILED)
      } else {
        // 对于历史消息，默认为成功状态
        // 包括: sendingState为0(未知)、1(成功)、undefined、null或其他值的情况
        deliveryStatus = 1; // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
        console.log('📜 历史消息状态设为成功:', {
          messageId: msgData.messageClientId || msgData.messageServerId,
          sendingState: msgData.sendingState,
          status: msgData.status,
          finalStatus: deliveryStatus
        });
      }
    } else {
      // 对方消息：成功状态
      deliveryStatus = 1;
    }

    const baseMessage = {
      messageId: messageId,
      senderId: senderId,
      targetId: msgData.receiverId || msgData.targetId,
      content: processedContent,
      msgType: this.convertMessageTypeToString(messageType),  // 🔥 转换为字符串类型 (text/image/audio/video/location/file)
      sendTime: timestamp,
      timestamp: timestamp,
      isSelf: isSelf,
      senderName: msgData.senderName || (isSelf ? '我' : '对方'),
      senderAvatar: senderAvatar,
      deliveryStatus: deliveryStatus,
      bubbleTime: this.formatBubbleTime(timestamp),
      _rawMessage: msgData // 🔥 保存 NIM SDK 原始消息对象，用于撤回等操作
    };

    // 🔥 调试：查看消息状态转换结果
    console.log('🔍 消息状态转换:', {
      isSelf: isSelf,
      sendingState: msgData.sendingState,
      status: msgData.status,
      deliveryStatus: baseMessage.deliveryStatus,
      messageId: baseMessage.messageId,
      messageType: 'formatMessage调用 - 历史消息处理'
    });

    // NIM SDK V2 撤回的消息会通过 onMessageRevokeNotifications 事件通知
    if (recalledFromServer) {
      return {
        ...baseMessage,
        isRecalled: true,
        msgType: 'system',  // 🔥 系统消息类型使用字符串
        content: isSelf ? '你撤回了一条消息' : (msgData.senderName ? `${msgData.senderName}撤回了一条消息` : '对方撤回了一条消息'),
        originalContent: processedContent
      };
    }

    // 文件消息补充文件展示字段，便于WXML直接使用
    if (messageType === 6) {  // 文件
      return {
        ...baseMessage,
        fileName: processedContent.fileName || msgData.fileName || '',
        fileSize: this.formatFileSize(processedContent.fileSize || msgData.fileSize || 0),
        fileType: processedContent.fileType || msgData.fileType || ''
      };
    }

    // 🔥 位置消息：为兼容旧渲染逻辑，将名称/地址/经纬度同步到根级字段，避免显示默认“位置”
    if (messageType === 4) {  // 位置
      return {
        ...baseMessage,
        locationName: processedContent?.locationName || msgData.locationName || '位置',
        locationAddress: processedContent?.address || msgData.locationAddress || '',
        latitude: processedContent?.latitude,
        longitude: processedContent?.longitude
      };
    }

    // 🔥 视频消息：将URL和缩略图同步到根级字段，兼容WXML渲染
    if (messageType === 3) {  // 视频
      return {
        ...baseMessage,
        content: processedContent.url || processedContent,  // WXML 中使用 item.content 作为 src
        thumbnail: processedContent.thumbnail || '',
        duration: processedContent.duration || 0,
        videoUrl: processedContent.url || ''
      };
    }

    // 🔥 最终调试输出
    console.log('✅ formatMessage 完成:', {
      messageId,
      msgType: baseMessage.msgType,  // 使用转换后的字符串类型
      msgTypeNumber: messageType,     // 原始数字类型（用于调试）
      content: processedContent,
      deliveryStatus: baseMessage.deliveryStatus,
      isSelf,
      timestamp
    });

    return baseMessage;
  },

  // 🔥 解析语音内容 - 支持 NIM SDK V2 的 attachment 对象格式
  parseVoiceContent(content) {
    try {
      if (!content) return { url: '', duration: 0 };

      // NIM SDK V2 的语音附件对象格式
      // attachment 通常包含: url, dur (duration), size 等字段
      if (typeof content === 'object' && content !== null) {
        const url = content.url || content.path || content.voiceUrl || '';
        const duration = content.dur || content.duration || 0;  // NIM SDK V2 使用 dur
        const size = content.size || content.fileSize || 0;

        console.log('🎤 语音附件对象解析:', { url, duration, size });
        return { url, duration, size };
      }

      // 如果是字符串，尝试JSON解析
      if (typeof content === 'string') {
        const trimmed = content.trim();
        if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
          try {
            const obj = JSON.parse(trimmed);
            const url = obj.url || obj.path || obj.voiceUrl || '';
            const duration = obj.dur || obj.duration || 0;
            const size = obj.size || obj.fileSize || 0;
            return { url, duration, size };
          } catch (e) {
            // 继续按URL兜底
          }
        }
        // 直接当作URL使用
        return { url: trimmed, duration: 0 };
      }

      return { url: '', duration: 0 };
    } catch (error) {
      console.error('❌ 解析语音内容失败:', error, content);
      return { url: '', duration: 0 };
    }
  },

  // 🔥 解析文件内容 - 支持 NIM SDK V2 的 attachment 对象格式
  parseFileContent(content) {
    try {
      if (!content) return { url: '', fileName: '', fileSize: 0 };

      // NIM SDK V2 的文件附件对象格式
      if (typeof content === 'object' && content !== null) {
        const url = content.url || content.path || content.file_url || content.fileUrl || '';
        const fileName = content.name || content.fileName || content.file_name || '';
        const fileSize = content.size || content.fileSize || content.file_size || 0;
        const ext = content.ext || content.fileType || content.file_type || '';

        console.log('📄 文件附件对象解析:', { url, fileName, fileSize, ext });
        return { url, fileName, fileSize, fileType: ext };
      }

      // JSON字符串
      if (typeof content === 'string') {
        const trimmed = content.trim();
        if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
          try {
            const obj = JSON.parse(trimmed);
            const url = obj.url || obj.path || obj.file_url || obj.fileUrl || '';
            const fileName = obj.name || obj.fileName || obj.file_name || '';
            const fileSize = obj.size || obj.fileSize || obj.file_size || 0;
            const fileType = obj.ext || obj.fileType || obj.file_type || '';
            return { url, fileName, fileSize, fileType };
          } catch (e) {
            // fallthrough to plain URL
          }
        }
        // 纯URL
        return { url: trimmed, fileName: '', fileSize: 0 };
      }

      return { url: '', fileName: '', fileSize: 0 };
    } catch (error) {
      console.error('❌ 解析文件内容失败:', error, content);
      return { url: '', fileName: '', fileSize: 0 };
    }
  },

  // 🔥 解析位置内容 - 支持 NIM SDK V2 的 attachment 对象格式
  parseLocationContent(content) {
    try {
      if (!content) return null;
      let obj = content;

      // NIM SDK V2 的位置附件对象格式
      // attachment 通常包含: lat, lng, title, address 等字段
      if (typeof content === 'string') {
        const trimmed = content.trim();
        if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
          try {
            obj = JSON.parse(trimmed);
          } catch (e) {
            return null;
          }
        } else {
          // 非JSON字符串，无法解析
          return null;
        }
      }

      // NIM SDK V2 使用 lat/lng，兼容 latitude/longitude
      const lat = obj.lat ?? obj.latitude;
      const lng = obj.lng ?? obj.longitude;
      const address = obj.address || obj.addr || '';
      const locationName = obj.title || obj.locationName || obj.name || '位置';

      console.log('📍 位置附件对象解析:', { lat, lng, address, locationName });

      if (lat == null || lng == null) return null;
      return { latitude: Number(lat), longitude: Number(lng), address, locationName };
    } catch (err) {
      console.error('❌ 解析位置内容失败:', err, content);
      return null;
    }
  },

  // 🔥 解析视频内容 - 支持 NIM SDK V2 的 attachment 对象格式
  parseVideoContent(content) {
    try {
      if (!content) return { url: '', thumbnail: '' };

      // NIM SDK V2 的视频附件对象格式
      if (typeof content === 'object' && content !== null) {
        const url = content.url || content.path || '';
        const thumbnail = content.thumbUrl || content.coverUrl || content.thumbnail || '';
        const duration = content.dur || content.duration || 0;
        const size = content.size || content.fileSize || 0;
        const width = content.width || content.w || 0;
        const height = content.height || content.h || 0;

        console.log('🎬 视频附件对象解析:', { url, thumbnail, duration, size, width, height });
        return { url, thumbnail, duration, size, width, height };
      }

      // JSON 字符串
      if (typeof content === 'string') {
        const trimmed = content.trim();
        if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
          try {
            const obj = JSON.parse(trimmed);
            const url = obj.url || obj.path || '';
            const thumbnail = obj.thumbUrl || obj.coverUrl || obj.thumbnail || '';
            const duration = obj.dur || obj.duration || 0;
            return { url, thumbnail, duration };
          } catch (e) {
            // 继续按URL兜底
          }
        }
        // 直接当作URL使用
        return { url: trimmed, thumbnail: '' };
      }

      return { url: '', thumbnail: '' };
    } catch (error) {
      console.error('❌ 解析视频内容失败:', error, content);
      return { url: '', thumbnail: '' };
    }
  },

  // 🔥 根据官方文档，NIM SDK V2 的正确枚举值（0=未知, 1=成功, 2=失败, 3=发送中）
  convertStatus(apiStatus) {
    // NIM SDK V2 的 V2NIMMessageSendingState 枚举：
    // V2NIM_MESSAGE_SENDING_STATE_UNKNOWN = 0 (未知状态)
    // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED = 1 (发送成功)
    // V2NIM_MESSAGE_SENDING_STATE_FAILED = 2 (发送失败)
    // V2NIM_MESSAGE_SENDING_STATE_SENDING = 3 (发送中)

    console.log('🔄 convertStatus 输入:', {
      apiStatus,
      type: typeof apiStatus,
      isUndefined: apiStatus === undefined,
      isNull: apiStatus === null
    });

    // 🔥 直接返回数字枚举值，不转换为字符串
    // undefined/null 默认为成功状态 1 (历史消息通常没有状态字段，说明已成功)
    if (apiStatus === undefined || apiStatus === null) {
      console.log('🔄 状态映射 convertStatus: undefined/null -> 1 (历史消息默认成功)');
      return 1; // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
    }

    // 如果已经是数字，直接返回
    if (typeof apiStatus === 'number') {
      // 验证是否为有效的 sendingState 枚举值 (0, 1, 2, 3)
      if (apiStatus === 0 || apiStatus === 1 || apiStatus === 2 || apiStatus === 3) {
        console.log('🔄 状态映射 convertStatus:', apiStatus, '(有效枚举值，保持不变)');
        return apiStatus;
      }
      // 对于历史消息，其他数字状态也默认为成功（因为能从服务器获取到说明已发送成功）
      console.log('🔄 状态映射 convertStatus:', apiStatus, '-> 1 (历史消息其他数字状态，默认成功)');
      return 1;
    }

    // 字符串状态转换为枚举值
    const s = String(apiStatus).toLowerCase();
    let result;
    if (s === 'succeeded' || s === 'success' || s === 'sent' || s === 'delivered') {
      result = 1; // SUCCEEDED
    } else if (s === 'failed' || s === 'fail') {
      result = 2; // FAILED
    } else if (s === 'sending') {
      result = 3; // SENDING
    } else {
      // 对于历史消息，未知状态默认为成功
      result = 1; // 默认成功
    }

    console.log('🔄 状态映射 convertStatus:', apiStatus, '->', result, '(历史消息处理)');
    return result;
  },

  // 🔥 解析图片内容 - 支持 NIM SDK V2 的 attachment 对象格式
  async parseImageContent(content) {
    try {

      if (!content) return { type: 'url', url: '' };

      // 1) NIM SDK V2 的 attachment 对象格式
      // attachment 通常包含: url, name, size, width, height 等字段
      if (typeof content === 'object') {
        // NIM SDK V2 图片附件字段
        const url = content.url || content.file_url || content.path || '';
        const fileName = content.name || content.fileName || content.file_name || '';
        const fileSize = content.size || content.fileSize || content.file_size || 0;
        const width = content.width || content.w || 0;
        const height = content.height || content.h || 0;

        console.log('🖼️ 图片附件对象解析:', { url, fileName, fileSize, width, height });

        if (/^data:image\//i.test(url)) {
          return { type: 'base64', url, fileName, fileSize, width, height };
        }
        if (url) {
          const cachedUrl = await imageCacheManager.preloadImage(url);
          return { type: 'url', url: cachedUrl, fileName, fileSize, width, height };
        }
        // 对象但无 url，视为错误
        return { type: 'error', url: '', error: '图片内容缺少URL', originalContent: JSON.stringify(content).slice(0, 200) };
      }

      // 2) 字符串：Base64
      if (typeof content === 'string' && /^data:image\//i.test(content)) {
        return { type: 'base64', url: content };
      }

      // 3) 字符串：URL
      if (typeof content === 'string' && /^https?:\/\//i.test(content)) {
        const cachedUrl = await imageCacheManager.preloadImage(content);
        return { type: 'url', url: cachedUrl };
      }

      // 4) 字符串：尝试 JSON 解析
      if (typeof content === 'string') {
        const s = content.trim();
        if (s.startsWith('{') && s.endsWith('}')) {
          try {
            const obj = JSON.parse(s);
            const u = obj.url || obj.file_url || obj.path || '';
            if (u) {
              const cachedUrl = await imageCacheManager.preloadImage(u);
              return {
                type: /^data:image\//i.test(u) ? 'base64' : 'url',
                url: cachedUrl,
                fileName: obj.name || obj.file_name || obj.fileName,
                fileSize: obj.size || obj.file_size || obj.fileSize,
                width: obj.width || obj.w,
                height: obj.height || obj.h
              };
            }
          } catch (e) {
            // fallthrough
          }
        }
      }

      // 5) 其他：无法识别，按错误返回，避免把纯文本当图片
      return { type: 'error', url: '', error: '图片内容无效', originalContent: String(content).slice(0, 200) };
    } catch (err) {
      console.error('❌ 解析图片内容失败:', err, content);
      return { type: 'error', url: '', error: '解析失败', originalContent: String(content).slice(0, 200) };
    }
  },

  // 🔥 新增：解析视频内容，统一返回可用于 <video src> 的字符串URL
  parseVideoContent(content) {
    try {
      if (!content) return '';

      // 对象格式
      if (typeof content === 'object') {
        const url = content.url || content.file_url || content.fileUrl || '';
        return String(url || '');
      }

      // 字符串格式：可能是JSON或URL
      const str = String(content).trim();
      if (!str) return '';
      if (str.startsWith('{') && str.endsWith('}')) {
        try {
          const obj = JSON.parse(str);
          const url = obj.url || obj.file_url || obj.fileUrl || '';
          return String(url || '');
        } catch (_) {
          // 继续按纯URL处理
        }
      }
      return str;
    } catch (e) {
      console.error('❌ 解析视频内容失败:', e, content);
      return '';
    }
  },

  // 判断是否显示时间
  shouldShowTime(timestamp) {
    // 简单实现：每5分钟显示一次时间
    const lastMessage = this.data.messages[this.data.messages.length - 1];
    if (!lastMessage) return true;

    const diff = timestamp - lastMessage.sendTime;
    return diff > 5 * 60 * 1000; // 5分钟
  },

  // 气泡内时间 HH:mm
  formatBubbleTime(timestamp) {
    const d = new Date(timestamp);
    const hh = d.getHours().toString().padStart(2, '0');
    const mm = d.getMinutes().toString().padStart(2, '0');
    return `${hh}:${mm}`;
  },

  // 日期标签：今天/昨天/YYYY/MM/DD
  formatDateLabel(timestamp) {
    const d = new Date(timestamp);
    const today = new Date();
    const yst = new Date();
    yst.setDate(today.getDate() - 1);
    const sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
    if (sameDay(d, today)) return '今天';
    if (sameDay(d, yst)) return '昨天';
    const y = d.getFullYear();
    const m = (d.getMonth() + 1).toString().padStart(2, '0');
    const day = d.getDate().toString().padStart(2, '0');
    return `${y}/${m}/${day}`;
  },

  // 🔥 过滤已删除的消息
  _filterDeleted(messages) {
    if (!Array.isArray(messages)) return [];
    return messages.filter(msg => !msg.isDeleted);
  },

  // 重新计算日期分隔（仅按日期），返回新数组
  recomputeDateDividers(list) {
    if (!Array.isArray(list)) return [];
    let lastDateKey = '';
    return list.map((m, idx) => {
      const d = new Date(m.timestamp || m.sendTime || 0);
      const key = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
      const show = key !== lastDateKey;
      const dateText = this.formatDateLabel(d.getTime());
      lastDateKey = key;
      return { ...m, showDateDivider: show, dateText, bubbleTime: m.bubbleTime || this.formatBubbleTime(m.timestamp || m.sendTime || Date.now()) };
    });
  },

  // 便捷设置消息并自动打日期分隔
  setMessagesWithDate(messages) {
    const safe = this._filterDeleted(messages);
    const annotated = this.recomputeDateDividers(safe);
    console.log('📋 setMessagesWithDate 执行:', {
      输入消息数: messages.length,
      过滤后消息数: safe.length,
      标注后消息数: annotated.length,
      最后一条消息: annotated[annotated.length - 1]
    });

    this.setData({ messages: annotated });
  },

  // 输入变化
  onInputChange(e) {
    const inputText = e.detail.value;
    this.setData({ inputText });
  },

  // 处理中文输入法开始
  onCompositionStart(e) {
    this.setData({ isComposing: true });
  },

  // 处理中文输入法结束
  onCompositionEnd(e) {
    this.setData({ isComposing: false });
  },

  // 处理文本框行数变化
  onLineChange(e) {

    // 可以在这里处理输入框高度变化的逻辑
  },

  // 显示媒体选择菜单
  showMediaPicker() {

    // 进入媒体选择时先收起工具栏，避免遮挡
    this.setData({
      showMorePanel: false,
      showEmojiPanel: false,
      emojiPanelHeight: 0,
      keyboardHeight: 0,
      showVoiceRecorder: false,
      inputType: 'text'
    });

    wx.showActionSheet({
      itemList: ['拍照', '拍摄视频'],
      success: (res) => {
        switch (res.tapIndex) {
          case 0:
            this.takePhoto();
            break;
          case 1:
            this.recordVideo();
            break;
        }
      },
      fail: (error) => {
        if (error.errMsg !== 'showActionSheet:fail cancel') {
          console.error('❌ 显示媒体选择菜单失败:', error);
        }
      }
    });
  },

  // 拍照
  async takePhoto() {

    try {
      // 开始拍照前关闭工具栏
      this.setData({ showMorePanel: false, showEmojiPanel: false, emojiPanelHeight: 0, keyboardHeight: 0, showVoiceRecorder: false, inputType: 'text' });

      const result = await mediaPicker.chooseImages({
        count: 1,
        sourceType: ['camera']
      });

      if (result.success && result.files.length > 0) {
        await this.sendImageMessage(result.files[0]);
      }
    } catch (error) {
      console.error('❌ 拍照失败:', error);
      wx.showToast({
        title: '拍照失败',
        icon: 'none'
      });
    }
  },

  // 从相册选择图片
  async chooseImageFromAlbum() {

    try {
      // 打开相册前先收起工具栏
      this.setData({ showMorePanel: false, showEmojiPanel: false, emojiPanelHeight: 0, keyboardHeight: 0, showVoiceRecorder: false, inputType: 'text' });

      // 在相册内二次分流：图片 或 视频
      const tapIndex = await new Promise((resolve) => {
        wx.showActionSheet({
          itemList: ['从相册选择图片', '从相册选择视频'],
          success: (res) => resolve(res.tapIndex),
          fail: (err) => {
            if (err.errMsg !== 'showActionSheet:fail cancel') {
              console.error('❌ 显示相册选择菜单失败:', err);
            }
            resolve(-1);
          }
        });
      });

      if (tapIndex === 0) {
        // 选择图片
        const result = await mediaPicker.chooseImages({
          count: 9,
          sourceType: ['album']
        });
        if (result.success && Array.isArray(result.files) && result.files.length > 0) {
          // 保险再截断一次
          const files = result.files.slice(0, 9);
          if (result.files.length > 9) {
            wx.showToast({ title: '最多发送9张图片', icon: 'none' });
          }
          for (const image of files) {
            await this.sendImageMessage(image);
          }
        }
      } else if (tapIndex === 1) {
        // 选择视频
        const result = await mediaPicker.chooseVideo({
          sourceType: ['album'],
          maxDuration: 60,
          compressed: true
        });
        if (result.success && Array.isArray(result.files) && result.files.length > 0) {
          const video = result.files[0];
          if (video.size && video.size > 200 * 1024 * 1024) {
            wx.showToast({ title: '视频超过200MB，无法发送', icon: 'none' });
            return;
          }
          // 简单压缩提示（如果支持，可扩展自定义压缩逻辑）
          if (!video.compressed && video.tempFilePath) {
            // 微信 chooseVideo 已有压缩，兜底提示

          }
          await this.sendVideoMessage(video);
        }
      }
    } catch (error) {
      console.error('❌ 处理相册选择发生异常:', error);
    }
  },

  // 拍摄视频
  async recordVideo() {

    try {
      // 开始录制前关闭工具栏
      this.setData({ showMorePanel: false, showEmojiPanel: false, emojiPanelHeight: 0, keyboardHeight: 0, showVoiceRecorder: false, inputType: 'text' });

      const result = await mediaPicker.chooseVideo({
        sourceType: ['camera'],
        maxDuration: 60
      });

      if (result.success && result.files.length > 0) {
        await this.sendVideoMessage(result.files[0]);
      }
    } catch (error) {
      console.error('❌ 拍摄视频失败:', error);
      wx.showToast({
        title: '拍摄视频失败',
        icon: 'none'
      });
    }
  },

  // 🔥 头像加载错误处理
  onAvatarError(e) {
    console.error('❌ 头像加载失败:', e.detail);


    // 🔥 尝试从dataset中获取更多信息
    const dataset = e.currentTarget.dataset;

  },

  // 🔥 头像加载成功处理
  onAvatarLoad(e) {

  },

  // 🔥 ===== 媒体消息发送功能 =====

  // 发送图片消息 - 兼容媒体选择器
  async sendImageMessage(imageFile) {

    try {
      // 如果传入的是字符串路径，转换为文件对象格式
      if (typeof imageFile === 'string') {
        imageFile = { path: imageFile };
      }

      // 检查用户信息
      if (!this.data.userInfo) {
        console.error('❌ 用户信息不存在，无法发送图片');
        return;
      }

      // 检查文件大小
      if (imageFile.size && imageFile.size > 10 * 1024 * 1024) { // 10MB限制
        wx.showToast({
          title: '图片大小不能超过10MB',
          icon: 'none'
        });
        return;
      }

      // 🔥 发送开始后立即收起工具栏/面板，回到聊天界面
      this.setData({
        showMorePanel: false,
        showEmojiPanel: false,
        emojiPanelHeight: 0,
        keyboardHeight: 0,
        showVoiceRecorder: false,
        inputType: 'text'
      });

      wx.showLoading({ title: '发送中...' });

      const imagePath = imageFile.path;
      console.log('📸 图片本地路径:', imagePath);

      // 🔥 获取当前用户的NIM账号ID
      const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
      if (!currentNimAccountId) {
        throw new Error('无法获取NIM账号ID');
      }

      // 🔥 创建临时图片消息
      const timestamp = Date.now();
      const tempMessage = {
        messageId: 'temp_image_' + timestamp,
        senderId: currentNimAccountId,
        targetId: this.data.targetId,
        content: {
          url: imagePath // 临时使用本地路径
        },
        msgType: 1,  // 图片
        sendTime: timestamp,
        timestamp: timestamp,
        isSelf: true,
        senderName: '我',
        senderAvatar: this.data.userAvatar || '',
        deliveryStatus: 3, // 发送中
        bubbleTime: this.formatBubbleTime(timestamp)
      };

      // 添加到消息列表
      const messages = this.data.messages.concat([tempMessage]);
      this.setMessagesWithDate(messages);
      setTimeout(() => this.scrollToBottom(), 100);

      // 🔥 使用 NIM SDK 发送图片消息（SDK会自动上传）
      const result = await nimConversationManager.sendImageMessage(
        this.data.conversationId,
        imagePath
      );

      wx.hideLoading();

      if (result && result.message) {
        // 🔥 格式化并替换临时消息
        const sentMessage = await this.formatMessage(result.message);
        sentMessage.deliveryStatus = 1; // 发送成功 (V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED)

        // 替换临时消息
        const tempIndex = this.data.messages.findIndex(msg => msg.messageId === tempMessage.messageId);
        if (tempIndex !== -1) {
          const updatedMessages = [...this.data.messages];
          updatedMessages[tempIndex] = sentMessage;
          this.setMessagesWithDate(updatedMessages);
          console.log('✅ 图片消息UI更新成功');
        }
      } else {
        throw new Error('NIM SDK 发送图片失败');
      }

    } catch (error) {
      wx.hideLoading();
      console.error('❌ 发送图片消息失败:', error);

      // 更新临时消息状态为失败
      const messages = this.data.messages.map(msg => {
        if (msg.messageId && msg.messageId.startsWith('temp_image_')) {
          return { ...msg, deliveryStatus: 1 }; // 发送失败
        }
        return msg;
      });
      this.setMessagesWithDate(messages);

      wx.showToast({
        title: '图片发送失败',
        icon: 'none'
      });
    }
  },

  // 发送视频消息 - 兼容媒体选择器
  async sendVideoMessage(videoFile) {

    try {
      // 如果传入的是字符串路径，转换为文件对象格式
      if (typeof videoFile === 'string') {
        videoFile = { path: videoFile };
      }

      // 检查用户信息
      if (!this.data.userInfo) {
        console.error('❌ 用户信息不存在，无法发送视频');
        return;
      }

      // 检查文件大小
      if (videoFile.size && videoFile.size > 200 * 1024 * 1024) { // 200MB限制
        wx.showToast({
          title: '视频不能超过200MB',
          icon: 'none'
        });
        return;
      }

      // 🔥 发送开始后立即收起工具栏/面板，回到聊天界面
      this.setData({
        showMorePanel: false,
        showEmojiPanel: false,
        emojiPanelHeight: 0,
        keyboardHeight: 0,
        showVoiceRecorder: false,
        inputType: 'text'
      });

      wx.showLoading({ title: '发送中...' });

      const videoPath = videoFile.path;
      const duration = videoFile.duration || 0; // 视频时长（秒）
      console.log('🎥 视频本地路径:', videoPath, '时长:', duration);

      // 🔥 获取当前用户的NIM账号ID
      const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
      if (!currentNimAccountId) {
        throw new Error('无法获取NIM账号ID');
      }

      // 🔥 创建临时视频消息
      const timestamp = Date.now();
      const tempMessage = {
        messageId: 'temp_video_' + timestamp,
        senderId: currentNimAccountId,
        targetId: this.data.targetId,
        content: {
          url: videoPath, // 临时使用本地路径
          duration: duration
        },
        msgType: 3,  // 视频
        sendTime: timestamp,
        timestamp: timestamp,
        isSelf: true,
        senderName: '我',
        senderAvatar: this.data.userAvatar || '',
        deliveryStatus: 3, // 发送中 (V2NIM_MESSAGE_SENDING_STATE_SENDING)
        bubbleTime: this.formatBubbleTime(timestamp)
      };

      // 添加到消息列表
      const messages = this.data.messages.concat([tempMessage]);
      this.setMessagesWithDate(messages);
      setTimeout(() => this.scrollToBottom(), 100);

      // 🔥 使用 NIM SDK 发送视频消息（SDK会自动上传）
      const result = await nimConversationManager.sendVideoMessage(
        this.data.conversationId,
        videoPath,
        duration * 1000 // NIM SDK 需要毫秒
      );

      wx.hideLoading();

      if (result && result.message) {
        // 🔥 格式化并替换临时消息
        const sentMessage = await this.formatMessage(result.message);
        sentMessage.deliveryStatus = 1; // 发送成功 (V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED)

        // 替换临时消息
        const tempIndex = this.data.messages.findIndex(msg => msg.messageId === tempMessage.messageId);
        if (tempIndex !== -1) {
          const updatedMessages = [...this.data.messages];
          updatedMessages[tempIndex] = sentMessage;
          this.setMessagesWithDate(updatedMessages);
          console.log('✅ 视频消息UI更新成功');
        }
      } else {
        throw new Error('NIM SDK 发送视频失败');
      }

    } catch (error) {
      wx.hideLoading();
      console.error('❌ 发送视频消息失败:', error);

      // 更新临时消息状态为失败
      const messages = this.data.messages.map(msg => {
        if (msg.messageId && msg.messageId.startsWith('temp_video_')) {
          return { ...msg, deliveryStatus: 1 }; // 发送失败
        }
        return msg;
      });
      this.setMessagesWithDate(messages);

      wx.showToast({
        title: '视频发送失败',
        icon: 'none'
      });
    }
  },

  // 🔥 ===== 消息发送功能 =====

  // 发送文本消息 - 使用 NIM SDK
  async sendTextMessage() {
    const content = this.data.inputText.trim();
    if (!content) return;

    // 检查用户信息
    if (!this.data.userInfo) {
      console.error('用户信息不存在，无法发送消息');
      wx.showToast({
        title: '用户信息错误',
        icon: 'none'
      });
      return;
    }

    // 清空输入框
    this.setData({
      inputText: '',
      showEmojiPanel: false,
      emojiPanelHeight: 0,
      showMorePanel: false
    });

    // 🔥 获取当前用户的NIM账号ID（用于临时消息的senderId，保持与实际消息一致）
    const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
    if (!currentNimAccountId) {
      console.error('无法获取NIM账号ID');
      wx.showToast({
        title: 'NIM账号错误',
        icon: 'none'
      });
      return;
    }

    // 创建临时消息（使用NIM账号ID作为senderId，确保与实际消息格式一致）
    const timestamp = Date.now();
    const tempMessage = {
      messageId: 'temp_' + timestamp,
      senderId: currentNimAccountId,  // 🔥 使用NIM账号ID，而不是customId
      targetId: this.data.targetId,
      content: content,
      msgType: 'text',  // 文本
      sendTime: timestamp,
      timestamp: timestamp,
      isSelf: true,
      senderName: '我',
      senderAvatar: this.data.userAvatar || '',
      deliveryStatus: 3,  // V2NIM_MESSAGE_SENDING_STATE_SENDING
      bubbleTime: this.formatBubbleTime(timestamp)
    };


    // 添加到消息列表
    const messages = this.data.messages.concat([tempMessage]);
    this.setMessagesWithDate(messages);

    // 滚动到底部
    setTimeout(() => this.scrollToBottom(), 100);

    try {
      console.log(' 准备发送文本消息 (NIM SDK):', {
        conversationId: this.data.conversationId,
        content: content
      });

      // 使用 NIM SDK 发送文本消息
      const result = await nimConversationManager.sendTextMessage(
        this.data.conversationId,
        content
      );

      if (result && result.message) {
        console.log('✅ 消息发送成功');

        const sentMessage = await this.formatMessage(result.message);
        sentMessage.deliveryStatus = 1; // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
        console.log('✅ 强制设置消息状态为成功 (deliveryStatus = 1)');

        // 🔥 使用临时消息的索引位置来替换，而不是依赖 messageId 匹配
        const tempIndex = this.data.messages.findIndex(msg => msg.messageId === tempMessage.messageId);

        if (tempIndex !== -1) {
          // 替换临时消息
          const updatedMessages = [...this.data.messages];
          updatedMessages[tempIndex] = sentMessage;
          this.setMessagesWithDate(updatedMessages);

          console.log('✅ 消息UI更新成功，新消息ID:', sentMessage.messageId);
        } else {
          // 临时消息未找到，直接添加实际消息
          console.warn('⚠️ 未找到临时消息，直接添加实际消息');
          const updatedMessages = this.data.messages.concat([sentMessage]);
          this.setMessagesWithDate(updatedMessages);
        }
      } else {
        throw new Error('NIM SDK 发送消息失败');
      }

    } catch (error) {
      console.error('发送消息失败:', error);
      // 更新消息状态为失败
      this.updateMessageDeliveryStatus(tempMessage.messageId, 1);  // V2NIM_MESSAGE_SENDING_STATE_FAILED
      wx.showToast({
        title: '发送失败',
        icon: 'none'
      });
    }
  },

  // 更新消息发送状态
  updateMessageDeliveryStatus(messageId, status) {
    const messages = this.data.messages.map(msg => {
      if (msg.messageId === messageId) {
        return { ...msg, deliveryStatus: status };
      }
      return msg;
    });
    this.setData({ messages });
  },

  // 标记所有消息已读（进入聊天页面时调用）
  async markAllMessagesRead() {
    try {
      if (!this.data.conversationId || this.data.conversationId.trim() === '') {
        console.log('⚠️ 会话ID为空，跳过标记已读');
        return;
      }

      console.log('📖 标记会话所有消息已读 (NIM SDK):', this.data.conversationId);

      // 使用 NIM SDK 标记已读
      await nimConversationManager.markConversationRead(this.data.conversationId);

      console.log('✅ 消息已全部标记已读');

      // 更新本地消息状态
      const updatedMessages = this.data.messages.map(msg => ({
        ...msg,
        deliveryStatus: msg.isSelf ? msg.deliveryStatus : 'read'
      }));

      this.setData({
        messages: updatedMessages
      });
    } catch (error) {
      console.error('❌ 标记消息已读失败:', error);
      // 不显示错误提示，避免影响用户体验
    }
  },

  // 切换输入类型
  toggleInputType() {
    const inputType = this.data.inputType === 'text' ? 'voice' : 'text';

    // 关闭表情面板和更多面板
    this.closeEmojiPanel();

    this.setData({
      inputType,
      showMorePanel: false,
      keyboardHeight: 0 // 切换输入类型时重置键盘高度
    });
  },

  // 🔥 ===== 语音录制触摸事件处理 =====

  // 开始录音（按下）
  onVoiceTouchStart(e) {
    console.log('🎤 开始录音');

    // 🔥 防抖检查：如果上次录音结束时间距离现在太近，忽略本次点击
    const now = Date.now();
    if (this.lastVoiceEndTime && (now - this.lastVoiceEndTime) < 500) {
      console.log('⚠️ 点击过快，忽略本次录音');
      wx.showToast({
        title: '要点坏了~~~~~',
        icon: 'none',
        duration: 1000
      });
      return;
    }

    // 🔥 如果正在录音中，忽略重复点击
    if (this.data.recording) {
      console.log('⚠️ 正在录音中，忽略重复点击');
      return;
    }

    // 设置录音状态
    this.setData({
      recording: true,
      voiceCancel: false
    });

    // 重置取消标记
    this.voiceRecordCancelled = false;

    // 🔥 延迟开始录音，防止误触
    this.voiceStartTimer = setTimeout(() => {
      // 再次检查是否仍在录音状态（用户可能已经松开了）
      if (this.data.recording && !this.voiceRecordCancelled) {
        this.startVoiceRecord();
      } else {
        console.log('⚠️ 用户已松开，取消录音启动');
        this.setData({ recording: false });
      }
    }, 150); // 延迟150ms开始录音
  },

  // 手指移动（判断是否取消）
  onVoiceTouchMove(e) {
    const touch = e.touches[0];
    const target = e.currentTarget;

    // 获取按钮位置信息
    const query = wx.createSelectorQuery();
    query.select('.voice-btn').boundingClientRect();
    query.exec((res) => {
      if (res && res[0]) {
        const rect = res[0];
        // 判断手指是否移出按钮区域（上滑取消）
        const isOutside = touch.clientY < rect.top - 50; // 上移50px以上取消

        this.setData({ voiceCancel: isOutside });

        if (isOutside) {
          console.log('⚠️ 上滑取消录音');
        }
      }
    });
  },

  // 结束录音（松开）
  onVoiceTouchEnd(e) {
    console.log('🎤 结束录音');

    // 🔥 清除延迟启动定时器
    if (this.voiceStartTimer) {
      clearTimeout(this.voiceStartTimer);
      this.voiceStartTimer = null;
    }

    const voiceCancel = this.data.voiceCancel;

    // 🔥 记录本次录音结束时间，用于防抖
    this.lastVoiceEndTime = Date.now();

    // 重置录音状态
    this.setData({
      recording: false,
      voiceCancel: false
    });

    if (voiceCancel) {
      // 🔥 设置取消标记，防止onStop回调发送消息
      this.voiceRecordCancelled = true;
      // 取消录音
      this.cancelVoiceRecord();
    } else {
      // 完成录音并发送
      this.stopVoiceRecord();
    }
  },

  // 录音被中断（如来电）
  onVoiceTouchCancel(e) {
    console.log('⚠️ 录音被中断');

    // 🔥 清除延迟启动定时器
    if (this.voiceStartTimer) {
      clearTimeout(this.voiceStartTimer);
      this.voiceStartTimer = null;
    }

    this.setData({
      recording: false,
      voiceCancel: false
    });

    // 🔥 设置取消标记
    this.voiceRecordCancelled = true;

    // 🔥 记录结束时间
    this.lastVoiceEndTime = Date.now();

    // 取消录音
    this.cancelVoiceRecord();
  },

  // 开始录音
  startVoiceRecord() {
    try {
      const recorderManager = wx.getRecorderManager();

      recorderManager.onStart(() => {
        console.log('✅ 录音开始');
        wx.vibrateShort(); // 震动反馈
      });

      // 🔥 在这里注册 onStop 回调,只注册一次
      recorderManager.onStop((res) => {
        console.log('✅ 录音结束 - 原始数据:', res);

        // 🔥 检查是否已取消录音
        if (this.voiceRecordCancelled) {
          console.log('⚠️ 录音已取消，不发送消息');
          this.voiceRecordCancelled = false; // 重置标记
          return;
        }

        console.log('📁 tempFilePath 详情:', {
          value: res.tempFilePath,
          type: typeof res.tempFilePath,
          length: res.tempFilePath?.length,
          exists: !!res.tempFilePath
        });

        const { tempFilePath, duration } = res;

        // 🔥 严格检查 tempFilePath
        if (!tempFilePath || typeof tempFilePath !== 'string' || tempFilePath.trim() === '') {
          console.error('❌ 录音文件路径无效:', tempFilePath);
          wx.showToast({
            title: '录音文件获取失败',
            icon: 'none'
          });
          return;
        }

        // 🔥 检查录音时长 - 至少需要1秒
        if (duration < 1000) {
          console.log('⚠️ 录音时间太短，不发送 - 时长:', duration);
          wx.showToast({
            title: '时间太短了，没录到啊~~~~~~',
            icon: 'none',
            duration: 1500
          });
          return;
        }

        console.log('✅ 准备发送语音，路径:', tempFilePath);

        // 🔥 使用微信文件系统获取文件信息验证
        wx.getFileSystemManager().getFileInfo({
          filePath: tempFilePath,
          success: (fileInfo) => {
            console.log('✅ 文件信息:', fileInfo);
            // 🔥 调用新的语音发送方法
            this.sendVoiceMessageWithNIM(tempFilePath, duration);
          },
          fail: (err) => {
            console.error('❌ 获取文件信息失败:', err);
            wx.showToast({
              title: '录音文件无效',
              icon: 'none'
            });
          }
        });
      });

      recorderManager.onError((err) => {
        console.error('❌ 录音错误:', err);
        wx.showToast({
          title: '录音失败',
          icon: 'none'
        });
        this.setData({ recording: false });
      });

      // 开始录音
      recorderManager.start({
        duration: 60000, // 最长60秒
        sampleRate: 16000,
        numberOfChannels: 1,
        encodeBitRate: 48000,
        format: 'mp3'
      });

      // 保存录音管理器实例
      this.recorderManager = recorderManager;

    } catch (error) {
      console.error('❌ 启动录音失败:', error);
      wx.showToast({
        title: '录音功能异常',
        icon: 'none'
      });
      this.setData({ recording: false });
    }
  },

  // 停止录音并发送
  stopVoiceRecord() {
    if (!this.recorderManager) {
      console.warn('⚠️ 录音管理器不存在');
      return;
    }

    try {
      // 🔥 直接停止录音，onStop回调已在startVoiceRecord中注册
      this.recorderManager.stop();
      console.log(' 停止录音，等待处理...');

    } catch (error) {
      console.error('❌ 停止录音失败:', error);
      wx.showToast({
        title: '录音失败',
        icon: 'none'
      });
    }
  },

  // 取消录音
  cancelVoiceRecord() {
    if (!this.recorderManager) {
      console.warn('⚠️ 录音管理器不存在');
      return;
    }

    try {
      // 🔥 先设置取消标记，确保onStop回调不会发送消息
      this.voiceRecordCancelled = true;

      this.recorderManager.stop();
      console.log('✅ 录音已取消');
      wx.showToast({
        title: '已取消',
        icon: 'none',
        duration: 1500
      });
    } catch (error) {
      console.error('❌ 取消录音失败:', error);
    }
  },

  // 🔥 发送语音消息（使用NIM SDK，新录制的语音）
  async sendVoiceMessageWithNIM(filePath, duration) {
    try {
      console.log('📤 准备发送语音消息 (NIM直接调用):', {
        filePath,
        duration,
        filePathType: typeof filePath,
        filePathLength: filePath?.length,
        targetId: this.data.targetId,
        chatType: this.data.chatType
      });

      // 🔥 严格检查参数
      if (!filePath || typeof filePath !== 'string' || filePath.trim() === '') {
        console.error('❌ 语音文件路径无效:', filePath);
        throw new Error('语音文件路径不能为空');
      }

      // 🔥 确保有会话ID（首次进入可能未设置）
      let conversationId = this.data.conversationId;
      if (!conversationId && this.data.targetId) {
        conversationId = this.buildNIMConversationId(this.data.targetId, this.data.chatType);
        this.setData({ conversationId });
        console.log('✅ 自动生成会话ID:', conversationId);
      }

      if (!conversationId) {
        console.error('❌ 会话ID为空');
        throw new Error('会话ID不能为空');
      }

      wx.showLoading({
        title: '发送中...'
      });

      // 🔥 获取当前用户的NIM账号ID
      const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
      if (!currentNimAccountId) {
        console.error('❌ 无法获取NIM账号ID');
        throw new Error('无法获取当前用户的NIM账号ID');
      }

      console.log('✅ 参数检查通过，准备创建临时消息');

      // 🔥 创建临时语音消息
      const timestamp = Date.now();
      const tempMessage = {
        messageId: 'temp_voice_' + timestamp,
        senderId: currentNimAccountId,
        targetId: this.data.targetId,
        content: {
          duration: duration,
          url: filePath // 临时使用本地路径
        },
        msgType: 'voice',
        sendTime: timestamp,
        timestamp: timestamp,
        isSelf: true,
        senderName: '我',
        senderAvatar: this.data.userAvatar || '',
        deliveryStatus: 3, // 发送中 (V2NIM_MESSAGE_SENDING_STATE_SENDING)
        bubbleTime: this.formatBubbleTime(timestamp)
      };

      // 添加到消息列表
      const messages = this.data.messages.concat([tempMessage]);
      this.setMessagesWithDate(messages);

      // 滚动到底部
      setTimeout(() => this.scrollToBottom(), 100);

      console.log('调用 NIM SDK 发送语音消息:', {
        conversationId: conversationId,
        targetId: this.data.targetId,
        filePath: filePath,
        duration: duration,
        isGroup: this.data.chatType === 1
      });

      // 🔥 使用 NIM SDK 发送语音消息（使用 conversationId 而不是 targetId）
      const result = await nimConversationManager.sendAudioMessage(
        conversationId,  // 🔥 修复：使用 conversationId 而不是 targetId
        filePath,
        duration, // 已经是毫秒
        {
          isGroup: this.data.chatType === 1
        }
      );

      wx.hideLoading();

      console.log('✅ 语音消息发送成功:', result);

      // 🔥 格式化并替换临时消息
      if (result && result.message) {
        const sentMessage = await this.formatMessage(result.message);
        sentMessage.deliveryStatus = 1; // 发送成功 (V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED)

        // 替换临时消息
        const tempIndex = this.data.messages.findIndex(msg => msg.messageId === tempMessage.messageId);
        if (tempIndex !== -1) {
          const updatedMessages = [...this.data.messages];
          updatedMessages[tempIndex] = sentMessage;
          this.setMessagesWithDate(updatedMessages);
          console.log('✅ 语音消息UI更新成功');
        }
      }

      wx.showToast({
        title: '发送成功',
        icon: 'success'
      });

    } catch (error) {
      wx.hideLoading();
      console.error('❌ 发送语音消息失败:', error);

      // 更新临时消息状态为失败
      const messages = this.data.messages.map(msg => {
        if (msg.messageId.startsWith('temp_voice_')) {
          return { ...msg, deliveryStatus: 1 }; // 发送失败
        }
        return msg;
      });
      this.setMessagesWithDate(messages);

      wx.showToast({
        title: '发送失败: ' + error.message,
        icon: 'none'
      });
    }
  },

  // 关闭表情面板的辅助方法
  closeEmojiPanel() {
    if (this.data.showEmojiPanel) {
      this.setData({
        showEmojiPanel: false,
        emojiPanelHeight: 0
      });
    }
  },

  // 收起输入法并重置相关状态
  hideInputMethod() {
    try { wx.hideKeyboard(); } catch (e) { /* 忽略异常 */ }
    this.setData({
      keyboardHeight: 0,
      inputFocus: false,
      showEmojiPanel: false,
      showMorePanel: false,
      emojiPanelHeight: 0
    });
  },

  // 切换表情面板
  toggleEmojiPanel() {
    const willShow = !this.data.showEmojiPanel;
    // 调用通用方法收起输入法
    this.hideInputMethod();
    this.setData({
      showEmojiPanel: willShow,
      showMorePanel: false
    }, () => {
      if (willShow) {
        // 表情面板展开后，测量其高度
        wx.createSelectorQuery()
          .select('.emoji-panel')
          .boundingClientRect(rect => {
            if (rect && rect.height) {
              console.log('😀 表情面板高度:', rect.height, rect.height - 10);
              this.setData({
                emojiPanelHeight: rect.height - 10 // 减去一些间距
              }, () => {
                // 滚动到底部，确保最后的消息可见
                this.scrollToBottom(true);
              });
            }
          })
          .exec();
      } else {
        // 表情面板关闭
        this.setData({
          emojiPanelHeight: 0
        });
      }
    });
  },

  // 切换更多面板
  toggleMorePanel() {
    const willShow = !this.data.showMorePanel;

    // 关闭表情面板
    this.closeEmojiPanel();
    // 调用通用方法收起输入法
    this.hideInputMethod();

    this.setData({
      showMorePanel: willShow
    });
  },

  // 阻止事件冒泡
  stopPropagation() {
    // 阻止事件冒泡，防止点击面板内容时关闭面板
  },

  // 选择表情
  selectEmoji(e) {
    const emoji = e.currentTarget.dataset.emoji;
    const inputText = this.data.inputText + emoji;
    this.setData({ inputText });
  },

  // 选择文件
  selectFile() {
    // 先收起面板
    this.setData({ showMorePanel: false });

    try {
      wx.chooseMessageFile({
        count: 1,
        type: 'file',
        success: async (res) => {
          if (!res || !res.tempFiles || res.tempFiles.length === 0) {
            wx.showToast({ title: '未选择文件', icon: 'none' });
            return;
          }

          const file = res.tempFiles[0];
          const filePath = file.path || file.tempFilePath;
          const fileName = file.name || '文件';
          const fileSize = file.size || 0;
          const readableSize = this.formatFileSize(fileSize);

          // 限制大小 200MB
          if (fileSize > 200 * 1024 * 1024) {
            wx.showToast({ title: '文件不能超过200MB', icon: 'none' });
            return;
          }

          // 获取当前用户的NIM账号ID
          const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
          if (!currentNimAccountId) {
            wx.showToast({ title: 'NIM账号错误', icon: 'none' });
            return;
          }

          // 创建临时消息
          const timestamp = Date.now();
          const clientTempId = 'temp_' + timestamp;
          const tempMessage = {
            messageId: clientTempId,
            senderId: currentNimAccountId,  // 🔥 使用NIM账号ID
            targetId: this.data.targetId,
            content: { url: '' },
            msgType: 'file',
            fileName: fileName,
            fileSize: readableSize,
            sendTime: timestamp,
            timestamp: timestamp,
            isSelf: true,
            senderName: '我',
            senderAvatar: this.data.userAvatar || '',
            deliveryStatus: 3,  // V2NIM_MESSAGE_SENDING_STATE_SENDING
            bubbleTime: this.formatBubbleTime(timestamp)
          };

          this.setMessagesWithDate(this.data.messages.concat([tempMessage]));
          setTimeout(() => this.scrollToBottom(), 100);

          wx.showLoading({ title: '发送中...' });

          try {
            // 🔥 使用 NIM SDK 发送文件消息（SDK会自动上传）
            const result = await nimConversationManager.sendFileMessage(
              this.data.conversationId,
              filePath
            );

            wx.hideLoading();

            if (result && result.message) {
              // 🔥 格式化并替换临时消息
              const sentMessage = await this.formatMessage(result.message);
              sentMessage.deliveryStatus = 1; // 发送成功 (V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED)

              const updatedMessages = this.data.messages.map(msg =>
                msg.messageId === tempMessage.messageId ? sentMessage : msg
              );
              this.setMessagesWithDate(updatedMessages);
              console.log('✅ 文件消息UI更新成功');
            } else {
              throw new Error('NIM SDK 发送文件失败');
            }
          } catch (err) {
            wx.hideLoading();
            console.error('❌ 发送文件失败:', err);
            // 更新为失败
            const failedMessages = this.data.messages.map(msg => {
              if (msg.messageId === tempMessage.messageId) {
                return { ...msg, deliveryStatus: 1 };  // V2NIM_MESSAGE_SENDING_STATE_FAILED
              }
              return msg;
            });
            this.setData({ messages: failedMessages });
            wx.hideLoading();
            wx.showToast({ title: '发送失败', icon: 'none' });
          }
        },
        fail: (error) => {
          if (error && error.errMsg && !/cancel/.test(error.errMsg)) {
            console.error('❌ 选择文件失败:', error);
            wx.showToast({ title: '选择文件失败', icon: 'none' });
          }
        }
      });
    } catch (e) {
      console.error('❌ 文件选择异常:', e);
      wx.showToast({ title: '选择文件异常', icon: 'none' });
    }
  },

  // 将字节大小格式化成人类可读
  formatFileSize(bytes) {
    try {
      if (!bytes || bytes <= 0) return '0 B';
      const units = ['B', 'KB', 'MB', 'GB', 'TB'];
      const i = Math.floor(Math.log(bytes) / Math.log(1024));
      const value = bytes / Math.pow(1024, i);
      return `${value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)} ${units[i]}`;
    } catch (e) {
      return `${bytes} B`;
    }
  },

  // 选择位置
  selectLocation() {
    wx.chooseLocation({
      success: async (res) => {
        try {

          const { name, address, latitude, longitude } = res || {};

          // 基本校验
          if (latitude == null || longitude == null) {
            wx.showToast({ title: '位置信息无效', icon: 'none' });
            return;
          }

          // 获取当前用户的NIM账号ID
          const currentNimAccountId = this.data.userInfo?.neteaseIMAccid || this.data.userInfo?.user?.neteaseIMAccid || '';
          if (!currentNimAccountId) {
            wx.showToast({ title: 'NIM账号错误', icon: 'none' });
            return;
          }

          // 构造临时消息
          const timestamp = Date.now();
          const clientTempId = 'temp_' + timestamp;
          const tempMsg = {
            messageId: clientTempId,
            senderId: currentNimAccountId,  // 🔥 使用NIM账号ID
            targetId: this.data.targetId,
            msgType: 'location',
            content: { latitude, longitude, address, locationName: name || '位置' },
            locationName: name || '位置',
            locationAddress: address || '',
            latitude,
            longitude,
            sendTime: timestamp,
            timestamp,
            isSelf: true,
            senderName: '我',
            senderAvatar: this.data.userAvatar || '',
            deliveryStatus: 3,  // V2NIM_MESSAGE_SENDING_STATE_SENDING
            bubbleTime: this.formatBubbleTime(timestamp)
          };

          this.setMessagesWithDate(this.data.messages.concat([tempMsg]));
          setTimeout(() => this.scrollToBottom(), 100);

          // 使用 NIM SDK 发送位置消息
          try {
            const result = await nimConversationManager.sendLocationMessage(
              this.data.conversationId,
              latitude,
              longitude,
              address || '',
              name || '位置'
            );

            if (result && result.message) {
              // 🔥 替换临时消息为正式消息
              console.log('✅ 位置消息发送成功，准备更新UI');

              const sentMessage = await this.formatMessage(result.message);

              // 🔥 强制设置为成功状态
              sentMessage.deliveryStatus = 1; // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED

              // 🔥 使用临时消息的索引位置来替换
              const tempIndex = this.data.messages.findIndex(msg => msg.messageId === clientTempId);

              if (tempIndex !== -1) {
                const updatedMessages = [...this.data.messages];
                updatedMessages[tempIndex] = sentMessage;
                this.setMessagesWithDate(updatedMessages);
              } else {
                const updatedMessages = this.data.messages.concat([sentMessage]);
                this.setMessagesWithDate(updatedMessages);
              }
            } else {
              throw new Error('NIM SDK 发送位置失败');
            }
          } catch (sendErr) {
            this.updateMessageDeliveryStatus(clientTempId, 1);  // V2NIM_MESSAGE_SENDING_STATE_FAILED
            throw sendErr;
          }

        } catch (err) {
          console.error('❌ 发送位置失败:', err);
          // 标记失败
          if (clientTempId) this.updateMessageDeliveryStatus(clientTempId, 1);  // V2NIM_MESSAGE_SENDING_STATE_FAILED
          wx.showToast({ title: '位置发送失败', icon: 'none' });
        }
      },
      fail: (e) => {
        if (e && e.errMsg && !/cancel/.test(e.errMsg)) {
          console.error('❌ 选择位置失败:', e);
          wx.showToast({ title: '选择位置失败', icon: 'none' });
        }
      },
      complete: () => {
        this.setData({ showMorePanel: false });
      }
    });
  },

  // 图片加载错误处理
  onImageError(e) {
    console.error('❌ 图片加载失败:', e.detail);
    const url = e.currentTarget.dataset.url;
    wx.showToast({
      title: '图片加载失败',
      icon: 'none',
      duration: 2000
    });
  },

  // 预览图片 - 支持新的图片格式
  previewImage(e) {
    const url = e.currentTarget.dataset.url;
    const type = e.currentTarget.dataset.type;

    if (!url) {
      wx.showToast({
        title: '图片地址无效',
        icon: 'none'
      });
      return;
    }

    // Base64格式需要特殊处理
    if (type === 'base64' || url.startsWith('data:image/')) {
      // 小程序的previewImage可以直接处理Base64
      wx.previewImage({
        current: url,
        urls: [url]
      });
    } else {
      // 普通URL格式
      wx.previewImage({
        current: url,
        urls: [url]
      });
    }
  },

  // 播放音频
  playAudio(e) {
    const message = e.currentTarget.dataset.message;

    // TODO: 实现音频播放
    wx.showToast({
      title: '音频播放功能开发中',
      icon: 'none'
    });
  },

  // 显示位置
  showLocation(e) {
    const message = e.currentTarget.dataset.message;

    try {
      // 统一解析位置内容（content 可能是 JSON 字符串或对象）
      const parsed = this.parseLocationContent(message.content) || {};
      const lat = message.latitude ?? parsed.latitude;
      const lng = message.longitude ?? parsed.longitude;
      const name = message.locationName || parsed.locationName || '位置';
      const address = message.locationAddress || parsed.address || '';

      if (lat == null || lng == null) {
        wx.showToast({ title: '无效位置', icon: 'none' });
        return;
      }

      wx.openLocation({
        latitude: Number(lat),
        longitude: Number(lng),
        name: name,
        address: address,
        scale: 16
      });
    } catch (err) {
      console.error('❌ 打开位置失败:', err);
      wx.showToast({ title: '打开位置失败', icon: 'none' });
    }
  },

  // 显示消息菜单
  showMessageMenu(e) {
    const message = e.currentTarget.dataset.message;
    const itemList = [];

    // 撤回的消息不显示菜单
    if (message.isRecalled) {
      return;
    }

    // 普通消息的菜单选项
    if (message.msgType === 'text') {
      itemList.push('复制');
    }

    if (message.isSelf) {
      // 检查是否可以撤回（2分钟内）
      const canRecall = this.canRecallMessage(message);
      if (canRecall) {
        itemList.push('撤回');
      }
    }

    // 如果没有可用操作，直接返回
    if (itemList.length === 0) {
      return;
    }

    wx.showActionSheet({
      itemList: itemList,
      success: (res) => {
        const action = itemList[res.tapIndex];
        switch (action) {
          case '复制':
            this.copyMessage(message);
            break;
          case '撤回':
            this.recallMessage(message);
            break;
        }
      }
    });
  },

  // 复制消息
  copyMessage(message) {
    if (message.msgType === 'text') {
      wx.setClipboardData({
        data: message.content,
        success: () => {
          wx.showToast({
            title: '已复制',
            icon: 'success'
          });
        }
      });
    }
  },

  // 撤回消息
  async recallMessage(message) {
    // 检查撤回时间限制
    if (!this.canRecallMessage(message)) {
      wx.showToast({
        title: '超过2分钟无法撤回',
        icon: 'none'
      });
      return;
    }

    wx.showModal({
      title: '确认撤回',
      content: '确定要撤回这条消息吗？',
      success: async (res) => {
        if (res.confirm) {
          try {
            // 显示加载状态
            wx.showLoading({
              title: '撤回中...',
              mask: true
            });

            // message._rawMessage 是保存的 NIM SDK 原始消息对象
            const rawMessage = message._rawMessage;
            if (!rawMessage) {
              throw new Error('消息对象不完整，无法撤回');
            }

            const result = await nimConversationManager.recallMessage(
              this.data.conversationId,
              rawMessage // 传入 NIM SDK 原始消息对象
            );

            if (result) {
              // 撤回成功，本地立即更新消息状态为已撤回
              this.updateMessageToRecalled(message.messageId);

              wx.showToast({
                title: '已撤回',
                icon: 'success'
              });
            } else {
              throw new Error('撤回失败');
            }
          } catch (error) {
            console.error('撤回消息失败:', error);
            wx.showToast({
              title: error.message || '撤回失败',
              icon: 'none'
            });
          } finally {
            wx.hideLoading();
          }
        }
      }
    });
  },

  // 检查是否可以撤回消息
  canRecallMessage(message) {
    if (!message.isSelf) return false;
    if (message.msgType === 'system' || message.isRecalled) return false;

    // 检查时间限制（2分钟 = 120秒）
    const now = Date.now();
    const messageTime = message.timestamp || message.createTime || now;
    const timeDiff = (now - messageTime) / 1000; // 转换为秒

    return timeDiff <= 120; // 2分钟内可撤回
  },

  // 更新消息为已撤回状态
  updateMessageToRecalled(messageId) {
    const messages = this.data.messages.map(msg => {
      if (msg.messageId === messageId) {
        const recallText = msg.isSelf ? '你撤回了一条消息' : '对方撤回了一条消息';

        return {
          ...msg,
          isRecalled: true,
          content: recallText,
          originalContent: msg.content,
          recallTime: Date.now(),
          msgType: 'text'
        };
      }
      return msg;
    });

    this.setMessagesWithDate(messages);

    // 同步撤回提示到本地，供会话列表页读取
    try {
      const convId = this.data.conversationId;
      if (convId) {
        const hints = wx.getStorageSync('recallHints') || {};
        // 使用实际的撤回消息文本，而不是固定的"你撤回了一条消息"
        const revokedMsg = messages.find(m => m.messageId === messageId);
        const previewText = revokedMsg?.content || '撤回了一条消息';
        hints[convId] = { preview: previewText, updatedAt: Date.now() };
        wx.setStorageSync('recallHints', hints);
      }
    } catch (e) {
      console.warn('写入撤回提示失败', e);
    }
  },

  // 重发消息
  async resendMessage(e) {
    const message = e.currentTarget.dataset.message;
    if (!message) return;
    console.log('🔁 重发消息 (NIM SDK):', message);

    // 标记为发送中
    this.updateMessageDeliveryStatus(message.messageId, 2);  // V2NIM_MESSAGE_SENDING_STATE_SENDING

    try {
      let result = null;
      const conversationId = this.data.conversationId;

      console.log('🔁 重发消息使用会话ID:', conversationId);

      switch (message.msgType) {
        case 'text':
          result = await nimConversationManager.sendTextMessage(
            conversationId,
            message.content
          );
          break;
        case 'image':
        case 'video':
        case 'voice':
        case 'file':
          // ⚠️ 图片/视频/语音/文件消息重发需要本地文件路径
          // 但发送失败的消息已经没有本地路径，无法重发
          wx.showToast({
            title: '媒体消息暂不支持重发',
            icon: 'none'
          });
          this.updateMessageDeliveryStatus(message.messageId, 1);
          return;
        case 'location': {
          const lat = message.latitude ?? message.content?.latitude;
          const lng = message.longitude ?? message.content?.longitude;
          const address = message.locationAddress || message.content?.address || '';
          const name = message.locationName || message.content?.locationName || '位置';
          result = await nimConversationManager.sendLocationMessage(
            conversationId,
            lat,
            lng,
            address,
            name
          );
          break;
        }
        default:
          throw new Error('不支持的消息类型');
      }

      if (result && result.message) {
        // 替换为新消息 - 使用索引定位避免ID不匹配
        const sentMessage = await this.formatMessage(result.message);

        // 🔥 强制设置为成功状态
        sentMessage.deliveryStatus = 1; // V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED

        const msgIndex = this.data.messages.findIndex(msg => msg.messageId === message.messageId);

        if (msgIndex !== -1) {
          const updatedMessages = [...this.data.messages];
          updatedMessages[msgIndex] = sentMessage;
          console.log('✅ 重发消息替换成功:', {
            原消息ID: message.messageId,
            新消息ID: sentMessage.messageId,
            索引位置: msgIndex
          });
          this.setMessagesWithDate(updatedMessages);
        } else {
          console.warn('⚠️ 未找到原消息，直接添加重发消息');
          this.setMessagesWithDate([...this.data.messages, sentMessage]);
        }
      } else {
        throw new Error('发送失败');
      }

    } catch (err) {
      console.error('❌ 重发失败:', err);
      this.updateMessageDeliveryStatus(message.messageId, 1);  // V2NIM_MESSAGE_SENDING_STATE_FAILED
      wx.showToast({ title: '发送失败', icon: 'none' });
    }
  },

  // 将消息置为失败并可选记录原因
  markMessageFailed(messageId, reason) {
    const messages = this.data.messages.map(m => m.messageId === messageId ? { ...m, deliveryStatus: 1, errorReason: reason || '' } : m);  // V2NIM_MESSAGE_SENDING_STATE_FAILED
    this.setData({ messages });
  },

  // 🔥 加载更多消息 - 仅在用户向上滚动时触发
  loadMoreMessages() {
    console.log('🔄 用户向上滚动，触发加载更多历史消息');
    console.log('🔍 当前状态检查:', {
      hasMore: this.data.hasMore,
      loadingMessages: this.data.loadingMessages,
      lastMessageTime: this.data.lastMessageTime,
      messagesCount: this.data.messages.length
    });

    // 🔥 检查是否还有更多消息
    if (!this.data.hasMore) {

      return;
    }

    // 🔥 检查是否正在加载中
    if (this.data.loadingMessages) {

      return;
    }

    // 🔥 检查是否有lastMessageTime用于分页
    if (!this.data.lastMessageTime) {
      console.log('⚠️ 缺少lastMessageTime，无法加载更多');
      this.setData({ hasMore: false });
      return;
    }

    console.log('✅ 开始加载更多历史消息');
    this.loadMessages(true); // 传递true表示是加载更多
  },

  // 滚动事件处理
  onScroll(e) {
    const { scrollTop, scrollHeight } = e.detail;
    const windowHeight = this.data.windowHeight - 200; // 减去输入框高度

    // 计算距离底部的距离
    const distanceFromBottom = scrollHeight - scrollTop - windowHeight;

    // 如果距离底部超过一个屏幕高度，显示滚动到底部按钮
    const shouldShow = distanceFromBottom > windowHeight;

    if (this.data.showScrollToBottom !== shouldShow) {
      this.setData({
        showScrollToBottom: shouldShow
      });
    }
  },

  // 🔥 滚动到底部 - 参考Flutter app的实现
  scrollToBottom() {
    if (this.data.messages.length === 0) return;

    const lastMessage = this.data.messages[this.data.messages.length - 1];
    if (!lastMessage) return;

    // 🔥 参考Flutter的animateTo(maxScrollExtent)实现
    // 方法1：使用scrollIntoView精确定位到最后一条消息
    const scrollIntoViewId = 'msg-' + lastMessage.messageId;

    this.setData({
      scrollIntoView: scrollIntoViewId,
      scrollTop: 999999, // 🔥 同时设置一个大值确保滚动到底部
      showScrollToBottom: false // 🔥 滚动到底部后隐藏按钮
    });

    // 🔥 延迟再次滚动，确保DOM完全渲染（模仿Flutter的延迟机制）
    setTimeout(() => {
      this.setData({
        scrollIntoView: scrollIntoViewId,
        scrollTop: 999999
      });
    }, 100);

    // 🔥 最后的保障：强制滚动到底部
    setTimeout(() => {
      this.setData({
        scrollTop: 999999
      });
    }, 300);
  },

  // 下载文件
  downloadFile(e) {
    const message = e.currentTarget.dataset.message;

    if (!message.content || !message.content.url) {
      wx.showToast({
        title: '文件地址无效',
        icon: 'none'
      });
      return;
    }

    wx.showModal({
      title: '下载文件',
      content: `确定要下载文件 "${message.fileName || '未知文件'}" 吗？`,
      success: (res) => {
        if (res.confirm) {
          wx.downloadFile({
            url: message.content.url,
            success: (downloadRes) => {
              if (downloadRes.statusCode === 200) {
                wx.openDocument({
                  filePath: downloadRes.tempFilePath,
                  success: () => {

                  },
                  fail: (err) => {
                    console.error('❌ 文件打开失败:', err);
                    wx.showToast({
                      title: '文件打开失败',
                      icon: 'none'
                    });
                  }
                });
              }
            },
            fail: (err) => {
              console.error('❌ 文件下载失败:', err);
              wx.showToast({
                title: '文件下载失败',
                icon: 'none'
              });
            }
          });
        }
      }
    });
  },

});


