// pages/chat-detail/chat-detail.js
// 聊天详情页面 - 与AI角色聊天

const app = getApp()
const api = require('../../utils/api')
const util = require('../../utils/util')
const proactiveMessage = require('../../utils/proactiveMessage')
const imageUrl = require('../../utils/imageUrl')
const config = require('../../config/index')

// 常用表情
const EMOJIS = [
  "😊", "😀", "😁", "😃", "😂", "🤣", "😅", "😆", "😉", "😋", "😎", "😍", "😘", "🥰", "😗", "😙",
  "🙂", "🤗", "🤩", "🤔", "😐", "😑", "😶", "🙄", "😏", "😣", "😥", "😮", "😯", "😪", "😫", "😴",
  "😌", "😛", "😜", "😝", "😒", "😓", "😔", "😕", "🙃", "😲", "😖", "😞", "😟", "😤", "😢", "😭",
  "😨", "😩", "😬", "😰", "😱", "😳", "😵", "😡", "😠", "😷", "🤒", "🤕", "😇", "🥳", "🥺",
  "👋", "👌", "✌️", "🤞", "👍", "👎", "👏", "🙌", "🤝", "🙏", "💪", "❤️", "🧡", "💛", "💚", "💙",
  "💜", "🖤", "💔", "💕", "💖", "💗", "💘", "💝", "🌹", "🌺", "🌻", "🌼", "🌷", "🎉", "🎊", "🎁"
]

Page({
  data: {
    statusBarHeight: 44,
    navHeight: 96,
    
    // 角色信息
    characterId: '',
    conversationId: '',
    character: {
      id: '',
      name: '加载中...',
      avatar: '',
      isOnline: true,
      job: '',
      location: '',
      gender: '',
      age: '',
      education: '',
      serviceCount: 0,
      returnCount: 0,
      rating: '4.9',
      motto: '',
      qualification: '',
      skills: [],
      introduction: ''
    },
    
    // 用户头像 - 从用户信息获取，使用默认头像
    myAvatar: '/images/default-avatar.svg',
    
    // 消息列表
    messages: [],
    
    // 输入状态
    inputText: '',
    inputFocus: false,
    isVoiceMode: false,
    isRecording: false,
    showEmoji: false,
    recordingDuration: 0,
    voiceCancelHint: false,
    
    // AI状态
    isTyping: false,
    isSending: false,
    playingVoiceId: null,
    
    // 滚动控制
    scrollIntoView: '',
    scrollTop: 0,           // 当前滚动位置
    
    // 加载状态
    loading: true,
    loadingMore: false,
    hasMore: true,
    page: 1,
    pageSize: 20,           // 每页加载20条消息
    isFirstLoad: true,      // 是否首次加载
    
    // 人物介绍弹窗
    showProfilePopup: false,
    
    // 查看评价弹窗
    showReviewPopup: false,
    reviews: [],
    
    // 更多功能面板
    showMorePanel: false,
    
    // 常用语列表
    quickReplies: [
      '你好，很高兴认识你~',
      '最近怎么样？',
      '有什么想聊的吗？',
      '今天心情如何？',
      '晚安，好梦~',
      '早安，新的一天开始了！'
    ],
    showQuickReplyPopup: false,
    
    // 表情列表
    emojis: EMOJIS,
    
    // 约时间弹窗
    showSchedulePopup: false,
    scheduleDate: '',
    scheduleTime: '',
    
    // 礼物相关
    showGiftPopup: false,
    giftList: [],
    selectedGift: null,
    userFlowers: 0,
    
    // 聊天配额相关
    remainingCount: 10,       // 剩余免费次数（已废弃，保留兼容）
    maxCount: 10,             // 每日最大免费次数（已废弃，保留兼容）
    isUnlocked: false,        // 是否已解锁该角色
    isVip: false,             // 是否为VIP用户
    showUnlockPopup: false,   // 显示解锁弹窗
    todayCharacterId: '',     // 今天已聊天的角色ID
    heartCount: 0,            // 用户爱心余额
    unlockHeartsCost: 500,    // 默认解锁爱心成本
    
    // 免费畅聊相关
    freeTime: null,
    countdownText: ''
  },

  onLoad(options) {
    const { statusBarHeight, navHeight } = app.globalData
    
    // 初始化消息处理相关变量
    this.pendingMessages = []
    this.messageTimer = null
    this.isProcessing = false
    
    // 获取参数
    const characterId = options.id || ''
    const conversationId = options.conversationId || ''
    const characterName = decodeURIComponent(options.name || '')
    
    // 设置用户头像
    const userInfo = app.globalData.userInfo
    const myAvatar = imageUrl.getAvatarUrl(userInfo?.avatar)
    
    this.setData({
      statusBarHeight,
      navHeight,
      characterId,
      conversationId,
      myAvatar,
      'character.name': characterName || '加载中...'
    })
    
    // 进入聊天详情页时，调用标记已读接口清除未读数
    if (conversationId) {
      this.markConversationAsRead(conversationId)
    }
    
    // 加载角色信息和聊天历史
    this.initChat()
  },

  onShow() {
    // 每次显示页面时，刷新一次配额状态，确保免费畅聊时间等状态是最新的
    if (!this.data.loading) {
      this.loadQuotaStatus()
    }
  },

  onUnload() {
    // 页面卸载时清理
    // 清除消息处理定时器
    if (this.messageTimer) {
      clearTimeout(this.messageTimer)
      this.messageTimer = null
    }
    // 清除图片回复定时器
    if (this.imageReplyTimers && this.imageReplyTimers.length > 0) {
      this.imageReplyTimers.forEach(timer => clearTimeout(timer))
      this.imageReplyTimers = []
    }
    // 清空待处理消息队列
    this.pendingMessages = []
    this.isProcessing = false
    
    // 离开页面时标记该角色的主动推送消息为已读
    if (this.data.characterId) {
      proactiveMessage.markAsRead(this.data.characterId)
    }
  },

  /**
   * 标记会话已读
   * 进入聊天详情页时调用，清除未读数
   * @param {string} conversationId - 会话ID
   */
  async markConversationAsRead(conversationId) {
    if (!conversationId) {
      console.log('[chat-detail] 没有conversationId，跳过标记已读')
      return
    }
    
    console.log('[chat-detail] 开始调用标记已读接口，conversationId:', conversationId)
    
    try {
      const res = await api.chat.markAsRead(conversationId)
      console.log('[chat-detail] 标记已读API响应:', JSON.stringify(res))
      
      if (res.code === 0 || res.success) {
        console.log('[chat-detail] 标记已读成功')
      } else {
        console.log('[chat-detail] 标记已读失败，响应:', res.message || res.error)
      }
    } catch (err) {
      console.error('[chat-detail] 标记已读请求异常:', err)
    }
  },

  /**
   * 初始化聊天
   */
  async initChat() {
    this.setData({ loading: true })
    
    try {
      // 检查登录状态和Token
      const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
      const userId = wx.getStorageSync(config.STORAGE_KEYS.USER_ID)
      console.log('[chat-detail] ========== 初始化聊天 ==========')
      console.log('[chat-detail] Token存在:', !!token)
      console.log('[chat-detail] Token长度:', token ? token.length : 0)
      console.log('[chat-detail] UserId:', userId)
      console.log('[chat-detail] CharacterId:', this.data.characterId)
      
      // 先加载配额状态
      await this.loadQuotaStatus()
      
      // 并行加载角色信息和聊天历史
      const [characterRes, historyRes] = await Promise.all([
        this.loadCharacterInfo(),
        this.loadChatHistory()
      ])
      
      this.setData({ loading: false })
      
      // 如果没有会话ID，创建新会话
      if (!this.data.conversationId && this.data.characterId) {
        await this.createConversation()
      }
    } catch (err) {
      console.error('初始化聊天失败', err)
      this.setData({ loading: false })
      util.showError('加载失败')
    }
  },

  /**
   * 加载聊天配额状态
   */
  async loadQuotaStatus() {
    const { characterId } = this.data
    
    if (!characterId) {
      console.log('[chat-detail] 没有角色ID，使用默认配额')
      return
    }
    
    try {
      const res = await api.chat.getQuota(characterId)
      console.log('[chat-detail] 配额API响应:', JSON.stringify(res))
      
      if (res && res.success && res.data) {
        const quota = res.data
        const isUnlocked = quota.is_unlocked || false
        const isVip = quota.isVip || (quota.free_chat_time && quota.free_chat_time.isVip) || false
        const freeChatTime = quota.free_chat_time || null
        
        const canChatByVip = !!isVip
        const canChatByFreeTime = !!(freeChatTime && freeChatTime.isActive)
        const canChat = isUnlocked || canChatByVip || canChatByFreeTime
        
        console.log('[chat-detail] 解析权限状态:', { isUnlocked, isVip, canChat, canChatByFreeTime })
        
        this.setData({
          remainingCount: -1,
          maxCount: 0,
          isUnlocked: !!isUnlocked,
          isVip: !!isVip,
          todayCharacterId: quota.today_character_id || '',
          freeTime: freeChatTime,
          unlockHeartsCost: quota.unlock_config?.hearts_cost || 500
        })
        
        // 处理免费畅聊倒计时
        if (freeChatTime && freeChatTime.isActive && freeChatTime.remainingSeconds > 0) {
          this.startCountdown(freeChatTime.remainingSeconds)
        } else {
          this.stopCountdown()
        }
        
        // 如果不能聊天且未解锁，显示解锁弹窗
        if (!canChat && !isUnlocked) {
          console.log('[chat-detail] 无聊天权限，显示解锁弹窗')
          this.setData({ showUnlockPopup: true })
        } else {
          console.log('[chat-detail] 拥有聊天权限，确保解锁弹窗关闭')
          this.setData({ showUnlockPopup: false })
        }
      }
    } catch (err) {
      console.log('[chat-detail] 加载权限状态失败', err)
      this.setData({
        remainingCount: -1,
        maxCount: 0,
        isUnlocked: false
      })
    }
    
    // 同时加载用户爱心值
    await this.loadHeartBalance()
  },

  /**
   * 开始倒计时
   */
  startCountdown(seconds) {
    this.stopCountdown()
    
    let remaining = seconds
    this.setData({
      countdownText: this.formatSeconds(remaining)
    })
    
    this.countdownTimer = setInterval(() => {
      remaining--
      if (remaining <= 0) {
        this.stopCountdown()
        this.setData({
          'freeTime.isActive': false,
          'freeTime.remainingSeconds': 0
        })
      } else {
        this.setData({
          countdownText: this.formatSeconds(remaining)
        })
      }
    }, 1000)
  },

  /**
   * 停止倒计时
   */
  stopCountdown() {
    if (this.countdownTimer) {
      clearInterval(this.countdownTimer)
      this.countdownTimer = null
    }
    this.setData({ countdownText: '' })
  },

  /**
   * 格式化秒数为 MM:SS
   */
  formatSeconds(s) {
    const m = Math.floor(s / 60)
    const rs = s % 60
    return `${m}:${rs < 10 ? '0' : ''}${rs}`
  },

  /**
   * 加载用户爱心值
   * 使用 /api/auth/me 接口，该接口从 im_users.grass_balance 读取余额
   */
  async loadHeartBalance() {
    try {
      const res = await api.auth.getCurrentUser()
      if (res.success && res.data) {
        this.setData({
          heartCount: res.data.grass_balance || 0
        })
        console.log('[chat-detail] 爱心值加载成功:', res.data.grass_balance)
      }
    } catch (err) {
      console.log('加载爱心值失败', err)
    }
  },

  /**
   * 加载角色信息
   */
  async loadCharacterInfo() {
    if (!this.data.characterId) return
    
    try {
      const res = await api.character.getDetail(this.data.characterId)
      
      if (res.success && res.data) {
        // 处理头像URL - 后端已返回完整URL，前端只需兜底处理
        const avatarUrl = imageUrl.getCharacterAvatarUrl(res.data.avatar || res.data.logo)
        
        // 解析擅长领域
        let skills = res.data.hobbiesTags || res.data.traits || []
        if (!Array.isArray(skills) || skills.length === 0) {
          skills = ['情感困惑', '职业压力', '成长创伤']
        }
        
        // 使用API返回的真实统计数据
        const serviceCount = res.data.serviceCount || 0
        const returnCount = res.data.returnCount || 0
        const avgRating = res.data.avgRating || 4.9
        
        this.setData({
          character: {
            id: res.data.id,
            name: res.data.name,
            avatar: avatarUrl,
            isOnline: true,
            voiceId: res.data.voice_id,
            job: res.data.occupation || res.data.companionType || '心理咨询师',
            location: res.data.location || res.data.province || '北京',
            gender: res.data.gender === 'male' ? '男' : '女',
            age: res.data.age || '90后',
            education: '本科',
            serviceCount: serviceCount,
            returnCount: returnCount,
            rating: avgRating.toFixed(2),
            motto: res.data.openingLine || '每一次倾诉，都是心灵的释放',
            qualification: '国家二级心理咨询师 | 情感咨询专家认证',
            skills: skills.slice(0, 3),
            introduction: res.data.selfIntroduction || res.data.about || '专业心理咨询培训'
          }
        })
      }
    } catch (err) {
      console.log('加载角色信息失败', err)
    }
  },

  /**
   * 加载聊天历史（首次加载最近20条）
   */
  async loadChatHistory() {
    const { characterId, pageSize } = this.data
    
    if (!characterId) {
      const welcomeMsg = {
        id: 'welcome',
        text: `你好！我是${this.data.character.name}，很高兴认识你~`,
        isMe: false,
        time: util.formatTime(new Date(), 'HH:mm'),
        type: 'text'
      }
      this.setData({ 
        messages: [welcomeMsg],
        isFirstLoad: false,
        hasMore: false
      })
      return
    }
    
    try {
      console.log('[chat-detail] 开始加载聊天历史, characterId:', characterId)
      
      // 首次只加载最近20条消息
      const res = await api.chat.getChatHistoryByCharacter(characterId, { 
        limit: pageSize,
        page: 1
      })
      
      console.log('[chat-detail] API响应:', JSON.stringify(res).substring(0, 200))
      
      if (res.success && res.data && res.data.length > 0) {
        console.log('[chat-detail] 收到历史消息数量:', res.data.length)
        
        const messages = res.data.map(msg => this.transformMessage(msg))
        
        this.setData({
          messages,
          hasMore: res.data.length >= pageSize,
          page: 1,
          isFirstLoad: false
        })
        
        console.log('[chat-detail] 消息已设置, 当前数量:', this.data.messages.length)
        console.log('[chat-detail] 首次加载完成，不自动滚动到底部')
      } else {
        console.log('[chat-detail] 没有历史记录，显示欢迎消息')
        const welcomeMsg = {
          id: 'welcome',
          text: `你好！我是${this.data.character.name}，很高兴认识你~`,
          isMe: false,
          time: util.formatTime(new Date(), 'HH:mm'),
          type: 'text'
        }
        this.setData({ 
          messages: [welcomeMsg],
          isFirstLoad: false,
          hasMore: false
        })
      }
    } catch (err) {
      console.log('加载聊天历史失败:', err)
      const welcomeMsg = {
        id: 'welcome',
        text: `你好！我是${this.data.character.name}，很高兴认识你~`,
        isMe: false,
        time: util.formatTime(new Date(), 'HH:mm'),
        type: 'text'
      }
      this.setData({ 
        messages: [welcomeMsg],
        isFirstLoad: false,
        hasMore: false
      })
    }
  },

  /**
   * 加载更多历史消息（向上翻页）
   */
  async loadMoreHistory() {
    const { characterId, loadingMore, hasMore, page, pageSize, messages } = this.data
    
    if (loadingMore || !hasMore || !characterId) {
      return
    }
    
    console.log('[chat-detail] 开始加载更多历史消息, page:', page + 1)
    
    this.setData({ loadingMore: true })
    
    try {
      const res = await api.chat.getChatHistoryByCharacter(characterId, { 
        limit: pageSize,
        page: page + 1
      })
      
      if (res.success && res.data && res.data.length > 0) {
        console.log('[chat-detail] 加载到更多消息:', res.data.length, '条')
        
        const newMessages = res.data.map(msg => this.transformMessage(msg))
        
        // 将新消息插入到列表开头（历史消息在前）
        this.setData({
          messages: [...newMessages, ...messages],
          hasMore: res.data.length >= pageSize,
          page: page + 1,
          loadingMore: false
        })
        
        console.log('[chat-detail] 历史消息加载完成，总消息数:', this.data.messages.length)
      } else {
        console.log('[chat-detail] 没有更多历史消息了')
        this.setData({
          hasMore: false,
          loadingMore: false
        })
      }
    } catch (err) {
      console.error('[chat-detail] 加载更多历史消息失败:', err)
      this.setData({ loadingMore: false })
    }
  },

  /**
   * 滚动事件监听（检测是否滚动到顶部）
   */
  onScroll(e) {
    const { scrollTop } = e.detail
    
    // 滚动到顶部时加载更多历史消息
    if (scrollTop < 50 && !this.data.loadingMore && this.data.hasMore) {
      console.log('[chat-detail] 滚动到顶部，触发加载更多')
      this.loadMoreHistory()
    }
  },

  /**
   * 转换消息格式
   */
  transformMessage(msg) {
    const baseMessage = {
      id: msg.id,
      text: msg.content,
      isMe: msg.role === 'user',
      time: util.formatTime(msg.created_at || msg.timestamp, 'HH:mm'),
      type: msg.message_type || 'text'
    }
    
    // 根据消息类型添加额外字段
    if (msg.message_type === 'image' && msg.image_url) {
      baseMessage.imageUrl = msg.image_url
    } else if (msg.message_type === 'voice' && msg.voice_url) {
      baseMessage.audioUrl = msg.voice_url
      baseMessage.duration = msg.voice_duration
    } else if (msg.message_type === 'gift' && msg.gift_info) {
      baseMessage.giftInfo = typeof msg.gift_info === 'string' ? JSON.parse(msg.gift_info) : msg.gift_info
    }
    
    return baseMessage
  },

  /**
   * 创建新会话
   * 注意：后端通过 Token 验证用户身份，不再需要传递 userId
   */
  async createConversation() {
    try {
      const res = await api.chat.createConversation(this.data.characterId)
      
      if (res.code === 0 && res.data) {
        this.setData({
          conversationId: res.data.id
        })
      } else if (res.success && res.data) {
        this.setData({
          conversationId: res.data.id
        })
      }
    } catch (err) {
      console.log('创建会话失败', err)
    }
  },

  /**
   * 返回上一页
   */
  onBack() {
    wx.navigateBack()
  },

  /**
   * 跳转到角色详情页
   */
  onGoToCharacterDetail() {
    const { characterId } = this.data
    if (characterId) {
      wx.navigateTo({
        url: `/pages/character-detail/character-detail?id=${characterId}`
      })
    }
  },

  /**
   * 用户头像加载失败时的处理
   */
  onAvatarError() {
    this.setData({
      myAvatar: '/images/default-avatar.svg'
    })
  },

  /**
   * 更多操作
   */
  onMore() {
    wx.showActionSheet({
      itemList: ['查看资料', '清空聊天记录', '举报'],
      success: (res) => {
        if (res.tapIndex === 0) {
          // 查看资料
          wx.navigateTo({
            url: `/pages/character-detail/character-detail?id=${this.data.characterId}`
          })
        } else if (res.tapIndex === 1) {
          this.clearMessages()
        } else if (res.tapIndex === 2) {
          util.showSuccess('举报已提交')
        }
      }
    })
  },

  /**
   * 清空消息
   * 只清空聊天记录，不删除会话
   * 会话仍然显示在消息列表中
   */
  async clearMessages() {
    const confirmed = await util.showConfirm({
      title: '清空记录',
      content: '确定要清空聊天记录吗？此操作不可恢复。'
    })
    
    if (!confirmed) return
    
    const { characterId } = this.data
    
    // 如果没有角色ID，只清空本地消息
    if (!characterId) {
      this.setData({ messages: [] })
      util.showSuccess('已清空')
      return
    }
    
    wx.showLoading({ title: '清空中...' })
    
    try {
      // 调用后端API清空聊天记录（使用角色ID）
      const res = await api.chat.clearChatHistory(characterId)
      
      if (res.success || res.code === 0) {
        // 清空本地消息列表
        this.setData({ messages: [] })
        util.showSuccess('已清空')
      } else {
        throw new Error(res.message || '清空失败')
      }
    } catch (err) {
      console.error('清空聊天记录失败:', err)
      // 即使API失败，也清空本地消息
      this.setData({ messages: [] })
      util.showSuccess('已清空')
    } finally {
      wx.hideLoading()
    }
  },

  /**
   * 输入文字
   */
  onInput(e) {
    this.setData({ inputText: e.detail.value })
  },

  /**
   * 发送消息
   */
  async onSend() {
    const { inputText, characterId, conversationId, character, isUnlocked, isVip, freeTime, remainingCount } = this.data
    
    // 只检查输入是否为空
    if (!inputText.trim()) return
    
    // 检查登录
    if (app.checkNeedLogin()) return
    
    // 检查聊天权限
    // 1. 如果已解锁或VIP，直接放行
    // 2. 如果未解锁且非VIP，检查免费畅聊时间
    const canChatByFreeTime = !!(freeTime && freeTime.isActive)
    const canChatByVip = !!isVip
    
    if (!isUnlocked && !canChatByVip && !canChatByFreeTime) {
      console.log('[chat-detail] 无聊天权限，显示解锁弹窗', { isUnlocked, isVip, canChatByFreeTime })
      this.setData({ showUnlockPopup: true })
      return
    }
    
    const messageText = inputText.trim()
    const newId = util.generateId()
    
    // 添加用户消息到列表
    const userMessage = {
      id: newId,
      text: messageText,
      isMe: true,
      time: util.formatTime(new Date(), 'HH:mm'),
      type: 'text'  // 标记为文字消息
    }
    
    // 立即清空输入框，允许用户继续输入
    this.setData({
      messages: [...this.data.messages, userMessage],
      inputText: ''
    }, () => {
      // 发送消息后立即滚动到底部
      this.scrollToBottom()
    })
    
    console.log('[chat-detail] 发送消息')
    
    // 将消息加入待处理队列
    this.pendingMessages.push(messageText)
    
    // 如果没有正在等待的定时器，启动延迟处理
    if (!this.messageTimer) {
      this.startMessageTimer(characterId, conversationId, character, isUnlocked, remainingCount)
    }
  },

  /**
   * 待处理消息队列
   */
  pendingMessages: [],
  messageTimer: null,
  isProcessing: false,

  /**
   * 启动消息处理定时器
   * 等待随机 2-8 秒，期间收集用户发送的所有消息
   */
  startMessageTimer(characterId, conversationId, character, isUnlocked, remainingCount) {
    // 随机延迟 2-4 秒
    const randomDelay = Math.floor(Math.random() * 2000) + 2000
    
    console.log('[chat-detail] 启动消息收集定时器，延迟:', randomDelay, 'ms')
    
    this.messageTimer = setTimeout(() => {
      this.messageTimer = null
      this.processPendingMessages(characterId, conversationId, character, isUnlocked, remainingCount)
    }, randomDelay)
  },

  /**
   * 处理待处理的消息队列
   * 将多条消息合并后发送给 AI
   */
  async processPendingMessages(characterId, conversationId, character, isUnlocked, remainingCount) {
    if (this.pendingMessages.length === 0) return
    
    // 如果正在处理中，延迟重试而不是直接丢弃消息
    if (this.isProcessing) {
      console.log('[chat-detail] 消息处理中，延迟500ms后重试')
      setTimeout(() => {
        this.processPendingMessages(characterId, conversationId, character, isUnlocked, remainingCount)
      }, 500)
      return
    }
    
    this.isProcessing = true
    
    // 取出所有待处理消息并清空队列
    const messagesToProcess = [...this.pendingMessages]
    this.pendingMessages = []
    
    // 合并多条消息为一条（用换行分隔）
    const combinedMessage = messagesToProcess.join('\n')
    
    console.log('[chat-detail] 合并处理消息:', messagesToProcess.length, '条')
    console.log('[chat-detail] 合并后内容:', combinedMessage)
    
    // 显示AI正在输入
    this.setData({ isTyping: true })
    
    try {
      // 构建对话历史（最近10条消息，只包含文字消息）
      // 过滤掉图片消息，因为后端不需要处理图片内容
      const conversationHistory = this.data.messages
        .slice(-10)
        .filter(msg => msg.type !== 'image' && msg.text) // 只保留有文字内容的消息
        .map(msg => ({
          role: msg.isMe ? 'user' : 'assistant',
          content: msg.text
        }))
      
      // 发送合并后的消息到后端
      const res = await api.chat.sendMessage({
        character_id: characterId,
        conversation_id: this.data.conversationId || conversationId,
        message: combinedMessage,
        conversationHistory: conversationHistory
      })
      
      this.setData({ isTyping: false })
      
      // 检查是否需要解锁
      if (!res.success && (res.error === 'FREE_CHAT_TIME_EXPIRED' || res.error === 'FREE_CHAT_TIME_NOT_CLAIMED')) {
        this.setData({ 
          showUnlockPopup: true,
          'freeTime.isActive': false
        })
        
        if (res.error === 'FREE_CHAT_TIME_NOT_CLAIMED') {
          wx.showModal({
            title: '领取免费畅聊',
            content: '领取100爱心值即可获得60分钟免费畅聊时间，是否现在去领取？',
            confirmText: '去领取',
            success: (modalRes) => {
              if (modalRes.confirm) {
                wx.switchTab({ url: '/pages/profile/profile' })
              }
            }
          })
        }
        
        this.isProcessing = false
        return
      }
      
      // 检查是否切换了角色
      if (!res.success && res.error === 'DIFFERENT_CHARACTER') {
        this.setData({ showUnlockPopup: true })
        wx.showToast({
          title: res.message || '今天已与其他角色聊天',
          icon: 'none'
        })
        this.isProcessing = false
        return
      }
      
      if (res.success && res.data) {
        // 更新会话ID（如果是新会话）
        if (res.data.conversation_id && !this.data.conversationId) {
          this.setData({ conversationId: res.data.conversation_id })
        }
        
        // 更新解锁状态（从后端返回的quota字段）
        if (res.data.quota) {
          const newIsUnlocked = res.data.quota.is_unlocked
          
          console.log('[chat-detail] 后端返回解锁状态:', { newIsUnlocked })
          
          this.setData({
            isUnlocked: newIsUnlocked || this.data.isUnlocked
          })
        }
        
        // 添加AI回复
        const aiMessage = {
          id: res.data.id || util.generateId(),
          text: res.data.content || res.data.message,
          isMe: false,
          time: util.formatTime(new Date(), 'HH:mm'),
          audioUrl: res.data.audio_url,
          type: 'text'  // 标记为文字消息
        }
        
        this.setData({
          messages: [...this.data.messages, aiMessage]
        }, () => {
          // AI回复后滚动到底部
          this.scrollToBottom()
        })
      } else {
        throw new Error(res.error || res.message || '发送失败')
      }
    } catch (err) {
      console.error('发送消息失败', err)
      
      // 开发模式下使用模拟AI回复
      const config = require('../../config/index')
      if (config.DEBUG) {
        console.log('[DEV] 使用模拟AI回复')
        const mockResponse = this.getMockAIResponse(combinedMessage, character.name)
        
        const aiMessage = {
          id: util.generateId(),
          text: mockResponse,
          isMe: false,
          time: util.formatTime(new Date(), 'HH:mm'),
          type: 'text'  // 标记为文字消息
        }
        
        this.setData({
          messages: [...this.data.messages, aiMessage],
          isTyping: false
        }, () => {
          this.scrollToBottom()
        })
        
        this.isProcessing = false
        return
      }
      
      this.setData({ isTyping: false })
      util.showError(err.message || '发送失败，请重试')
    }
    
    this.isProcessing = false
  },

  /**
   * 获取模拟AI回复（开发模式）
   */
  getMockAIResponse(userMessage, characterName) {
    const responses = [
      `嗯，我明白你的意思~`,
      `这个问题很有趣呢，让我想想...`,
      `谢谢你愿意和我分享这些~`,
      `我觉得你说得很有道理！`,
      `哈哈，你真的很有趣~`,
      `我一直都在这里陪着你哦~`,
      `能和你聊天真的很开心！`,
      `你今天心情怎么样呀？`,
      `我很喜欢和你聊天的感觉~`,
      `嗯嗯，我在认真听你说呢~`
    ]
    
    // 根据用户消息内容选择合适的回复
    if (userMessage.includes('你好') || userMessage.includes('嗨') || userMessage.includes('hi')) {
      return `你好呀！我是${characterName}，很高兴认识你~今天想聊点什么呢？`
    }
    if (userMessage.includes('名字') || userMessage.includes('叫什么')) {
      return `我叫${characterName}呀，你可以这样叫我~`
    }
    if (userMessage.includes('喜欢')) {
      return `我也很喜欢和你聊天呢！你喜欢什么呀？`
    }
    if (userMessage.includes('开心') || userMessage.includes('高兴')) {
      return `看到你开心我也很开心呢！希望你每天都这么快乐~`
    }
    if (userMessage.includes('难过') || userMessage.includes('伤心') || userMessage.includes('不开心')) {
      return `抱抱你~有什么不开心的事情可以和我说说，我会一直陪着你的。`
    }
    
    // 随机选择一个回复
    const randomIndex = Math.floor(Math.random() * responses.length)
    return responses[randomIndex]
  },

  /**
   * 滚动到底部（仅在发送/接收新消息时调用）
   * 使用 scroll-into-view 属性，自动滚动到最后一条消息
   */
  scrollToBottom() {
    const messages = this.data.messages
    if (messages && messages.length > 0) {
      // 使用 setTimeout 确保 DOM 已更新
      setTimeout(() => {
        this.setData({
          scrollIntoView: `msg-${messages.length - 1}`
        })
      }, 100)
    }
  },

  /**
   * 滚动到顶部（加载更多历史消息后保持位置）
   */
  scrollToTop() {
    this.setData({
      scrollTop: 0
    })
  },

  /**
   * 切换语音模式
   */
  onVoiceMode() {
    this.setData({
      isVoiceMode: !this.data.isVoiceMode,
      showEmoji: false
    })
  },

  /**
   * 开始录音
   */
  onVoiceStart() {
    this.setData({ isRecording: true })
    
    wx.showToast({
      title: '正在录音...',
      icon: 'none',
      duration: 60000
    })
    
    // 开始录音
    const recorderManager = wx.getRecorderManager()
    recorderManager.start({
      duration: 60000,
      format: 'mp3'
    })
    
    this.recorderManager = recorderManager
  },

  /**
   * 结束录音
   */
  onVoiceEnd() {
    this.setData({ isRecording: false })
    wx.hideToast()
    
    if (this.recorderManager) {
      this.recorderManager.stop()
      
      this.recorderManager.onStop((res) => {
        // 发送语音消息（暂时转为文字）
        const newId = util.generateId()
        const voiceMessage = {
          id: newId,
          text: '[语音消息]',
          isMe: true,
          time: util.formatTime(new Date(), 'HH:mm'),
          type: 'voice',
          audioUrl: res.tempFilePath
        }
        
        this.setData({
          messages: [...this.data.messages, voiceMessage]
        })
        
        this.scrollToBottom()
      })
    }
  },

  /**
   * 切换表情面板
   */
  onEmojiToggle() {
    // 收起键盘
    this.setData({
      inputFocus: false
    })
    
    // 延迟一点再显示面板，确保键盘先收起
    setTimeout(() => {
      this.setData({
        showEmoji: !this.data.showEmoji,
        showMorePanel: false,
        isVoiceMode: false
      })
    }, 50)
  },

  /**
   * 点击+号，显示更多功能面板
   */
  onAddMore() {
    // 收起键盘
    this.setData({
      inputFocus: false
    })
    
    // 延迟一点再显示面板，确保键盘先收起
    setTimeout(() => {
      this.setData({
        showMorePanel: !this.data.showMorePanel,
        showEmoji: false,
        isVoiceMode: false
      })
    }, 50)
  },

  /**
   * 关闭所有面板
   */
  onClosePanels() {
    this.setData({
      showEmoji: false,
      showMorePanel: false
    })
  },

  /**
   * 点击聊天区域关闭面板
   */
  onTapChatArea() {
    if (this.data.showEmoji || this.data.showMorePanel) {
      this.setData({
        showEmoji: false,
        showMorePanel: false
      })
    }
  },

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

  /**
   * 播放语音消息
   */
  onPlayVoice(e) {
    const { id, url } = e.currentTarget.dataset
    
    if (!url) {
      util.showError('语音不可用')
      return
    }
    
    const innerAudioContext = wx.createInnerAudioContext()
    innerAudioContext.src = url
    innerAudioContext.play()
    
    innerAudioContext.onEnded(() => {
      innerAudioContext.destroy()
    })
  },

  /**
   * 播放AI语音
   */
  async onPlayAIVoice(e) {
    const { id, text } = e.currentTarget.dataset
    const { character } = this.data
    
    if (!character.voiceId) {
      util.showError('该角色暂不支持语音')
      return
    }
    
    util.showLoading('生成语音中...')
    
    try {
      const res = await api.tts.synthesize({
        text: text,
        voice_id: character.voiceId,
        character_id: character.id
      })
      
      util.hideLoading()
      
      if (res.success && res.data && res.data.audio_url) {
        const innerAudioContext = wx.createInnerAudioContext()
        innerAudioContext.src = res.data.audio_url
        innerAudioContext.play()
      } else {
        util.showError('语音生成失败')
      }
    } catch (err) {
      util.hideLoading()
      util.showError('语音生成失败')
    }
  },

  /**
   * 开始聊天（免费倾诉）
   */
  onStartChat() {
    // 聚焦输入框
    this.setData({ showEmoji: false })
  },

  /**
   * 显示人物介绍弹窗
   */
  onShowProfile() {
    this.setData({ showProfilePopup: true })
  },

  /**
   * 关闭人物介绍弹窗
   */
  onCloseProfile() {
    this.setData({ showProfilePopup: false })
  },

  /**
   * 查看评价
   */
  onShowReviews() {
    // 生成模拟评价数据
    const mockReviews = [
      {
        id: 1,
        phone: '138****6172',
        date: '2024-12-15 14:32',
        content: '老师很有耐心，倾听我的问题后给出了很中肯的建议。咨询后感觉心里轻松了很多，对未来也有了新的规划...',
        tags: ['专业', '耐心', '有效果'],
        reply: '谢谢您的信任，很高兴能够帮助到您。希望您能继续保持积极的心态，有任何问题随时可以来找我交流。',
        likes: 23
      },
      {
        id: 2,
        phone: '186****3298',
        date: '2024-12-10 09:15',
        content: '第一次尝试心理咨询，老师非常专业，让我感觉很放松。通过几次咨询，我对自己的情绪有了更好的认识...',
        tags: ['专业', '温暖', '有帮助'],
        reply: '能够陪伴您成长是我的荣幸，继续加油！',
        likes: 15
      },
      {
        id: 3,
        phone: '159****7721',
        date: '2024-12-05 20:48',
        content: '老师的声音很温柔，聊天的过程中感觉很舒服。虽然问题还在，但是心态好了很多，会继续找老师咨询的...',
        tags: ['温柔', '善于倾听'],
        reply: '',
        likes: 8
      },
      {
        id: 4,
        phone: '177****4532',
        date: '2024-11-28 16:22',
        content: '咨询师很专业，能够快速理解我的问题并给出建议。性价比很高，会推荐给朋友...',
        tags: ['专业', '高效'],
        reply: '',
        likes: 12
      },
      {
        id: 5,
        phone: '133****8965',
        date: '2024-11-20 11:05',
        content: '非常好的一次体验，老师很有同理心，让我感受到了被理解和支持...',
        tags: ['有同理心', '支持'],
        reply: '感谢您的认可，祝您生活愉快！',
        likes: 6
      }
    ]
    
    this.setData({
      showReviewPopup: true,
      reviews: mockReviews
    })
  },

  /**
   * 关闭评价弹窗
   */
  onCloseReviews() {
    this.setData({ showReviewPopup: false })
  },

  /**
   * 阻止弹窗滚动穿透
   */
  preventMove() {
    return false
  },

  /**
   * 关闭解锁弹窗
   */
  closeUnlockPopup() {
    this.setData({ showUnlockPopup: false })
  },

  /**
   * 跳转到个人中心（去领取奖励）
   */
  onGoToProfile() {
    wx.switchTab({
      url: '/pages/profile/profile'
    })
  },

  /**
   * 爱心兑换解锁
   */
  async onExchangeHearts() {
    const { character, heartCount, unlockHeartsCost } = this.data
    const config = require('../../config/index')
    
    // 检查登录
    const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
    if (!token) {
      wx.showToast({ title: '请先登录', icon: 'none' })
      setTimeout(() => {
        wx.navigateTo({ url: '/pages/login/login' })
      }, 1500)
      return
    }

    // 检查爱心值，不足时提示并跳转充值页面
    if (heartCount < unlockHeartsCost) {
      wx.showToast({ title: '爱心值不足，去充值', icon: 'none' })
      setTimeout(() => {
        this.setData({ showUnlockPopup: false })
        wx.navigateTo({ url: '/pages/recharge/recharge' })
      }, 1500)
      return
    }

    wx.showLoading({ title: '兑换中...' })

    try {
      const res = await api.character.unlock({
        character_id: character.id,
        unlock_type: 'hearts'
      })

      wx.hideLoading()

      if (res.success || res.code === 0) {
        wx.showToast({ title: '解锁成功', icon: 'success' })
        
        // 更新状态，使用后端返回的剩余爱心数
        const remainingHearts = res.data?.remaining_hearts ?? (heartCount - unlockHeartsCost)
        this.setData({
          heartCount: remainingHearts,
          isUnlocked: true,
          remainingCount: -1, // -1 表示无限
          showUnlockPopup: false
        })
      } else {
        wx.showToast({ title: res.message || '兑换失败', icon: 'none' })
      }
    } catch (err) {
      wx.hideLoading()
      console.error('爱心兑换失败', err)
      wx.showToast({ title: '网络错误，请重试', icon: 'none' })
    }
  },

  /**
   * 直接购买解锁（9.9元）
   * 使用 /api/payment/unified-order 接口
   * 测试模式下返回 testMode: true，订单直接完成，无需调用微信支付
   */
  async onPurchaseDirect() {
    const { character } = this.data
    const config = require('../../config/index')
    
    // 检查登录
    const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
    if (!token) {
      wx.showToast({ title: '请先登录', icon: 'none' })
      setTimeout(() => {
        wx.navigateTo({ url: '/pages/login/login' })
      }, 1500)
      return
    }

    wx.showLoading({ title: '创建订单中...' })

    try {
      // 调用统一支付订单接口
      const res = await api.payment.createUnifiedOrder({
        type: 'character_unlock',
        character_id: character.id,
        amount: 9.9
      })

      wx.hideLoading()
      
      console.log('[chat-detail onPurchaseDirect] API返回:', JSON.stringify(res))

      if (res.success) {
        // 检查是否为测试模式（兼容多种判断方式）
        const isTestMode = res.testMode || res.test_mode || res.data?.testMode || res.data?.test_mode ||
          // 如果 payParams.package 包含 mock_prepay，也认为是测试模式
          (res.payParams?.package && res.payParams.package.includes('mock_prepay'))
        
        if (isTestMode) {
          // 测试模式：订单已直接完成，无需调用微信支付
          wx.showToast({ title: '购买成功', icon: 'success' })
          
          // 重新加载配额状态
          await this.loadQuotaStatus()
          
          // 更新状态
          this.setData({
            isUnlocked: true,
            remainingCount: -1,
            showUnlockPopup: false
          })
        } else {
          // 正式模式：调用微信支付
          const payParams = res.payParams || res.pay_params || res.data?.payParams || res.data?.pay_params
          
          if (!payParams || !payParams.timeStamp) {
            wx.showToast({ title: '支付参数错误', icon: 'none' })
            return
          }
          
          wx.requestPayment({
            timeStamp: payParams.timeStamp,
            nonceStr: payParams.nonceStr,
            package: payParams.package,
            signType: payParams.signType || 'RSA',
            paySign: payParams.paySign,
            success: async () => {
              wx.showToast({ title: '解锁成功', icon: 'success' })
              
              // 重新加载配额状态
              await this.loadQuotaStatus()
              
              // 更新状态
              this.setData({
                isUnlocked: true,
                remainingCount: -1,
                showUnlockPopup: false
              })
            },
            fail: (err) => {
              if (err.errMsg !== 'requestPayment:fail cancel') {
                wx.showToast({ title: '支付失败', icon: 'none' })
              }
            }
          })
        }
      } else {
        wx.showToast({ title: res.message || '创建订单失败', icon: 'none' })
      }
    } catch (err) {
      wx.hideLoading()
      console.error('购买解锁失败', err)
      wx.showToast({ title: '网络错误，请重试', icon: 'none' })
    }
  },

  /**
   * 拍照
   */
  onTakePhoto() {
    this.setData({ showMorePanel: false })
    
    wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sourceType: ['camera'],
      camera: 'back',
      success: (res) => {
        const tempFilePath = res.tempFiles[0].tempFilePath
        this.sendImageMessage(tempFilePath)
      },
      fail: (err) => {
        if (err.errMsg !== 'chooseMedia:fail cancel') {
          util.showError('拍照失败')
        }
      }
    })
  },

  /**
   * 从相册选择图片
   */
  onChooseImage() {
    this.setData({ showMorePanel: false })
    
    wx.chooseMedia({
      count: 9,
      mediaType: ['image'],
      sourceType: ['album'],
      success: (res) => {
        res.tempFiles.forEach(file => {
          this.sendImageMessage(file.tempFilePath)
        })
      },
      fail: (err) => {
        if (err.errMsg !== 'chooseMedia:fail cancel') {
          util.showError('选择图片失败')
        }
      }
    })
  },

  /**
   * 发送图片消息
   * 发送图片后，先上传到服务器，然后保存到数据库，最后AI返回预设的图片回复话术
   */
  async sendImageMessage(tempFilePath) {
    const newId = util.generateId()
    
    // 先添加本地消息（显示上传中状态）
    const imageMessage = {
      id: newId,
      type: 'image',
      imageUrl: tempFilePath,
      isMe: true,
      time: util.formatTime(new Date(), 'HH:mm'),
      uploading: true  // 标记为上传中
    }
    
    this.setData({
      messages: [...this.data.messages, imageMessage]
    }, () => {
      this.scrollToBottom()
    })
    
    try {
      // 1. 上传图片到服务器
      console.log('[chat-detail] 开始上传图片:', tempFilePath)
      
      // 检查登录状态
      const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
      console.log('[chat-detail] Token存在:', !!token)
      console.log('[chat-detail] Token长度:', token ? token.length : 0)
      
      const uploadRes = await api.uploadFile(tempFilePath, 'uploads')
      
      if (!uploadRes || !uploadRes.success || !uploadRes.data || !uploadRes.data.url) {
        throw new Error('图片上传失败')
      }
      
      const imageUrl = uploadRes.data.url
      console.log('[chat-detail] 图片上传成功:', imageUrl)
      
      // 2. 更新本地消息，移除上传中状态
      const messages = this.data.messages.map(msg => {
        if (msg.id === newId) {
          return { ...msg, imageUrl: imageUrl, uploading: false }
        }
        return msg
      })
      this.setData({ messages })
      
      // 3. 保存图片消息到数据库
      try {
        await api.chat.sendImage({
          character_id: this.data.characterId,
          conversation_id: this.data.conversationId,
          image_url: imageUrl
        })
        console.log('[chat-detail] 图片消息已保存到数据库')
      } catch (err) {
        console.error('[chat-detail] 保存图片消息失败:', err)
        // 保存失败不影响显示，继续执行
      }
      
      // 4. 使用独立的图片回复定时器，避免与文字消息冲突
      // 随机延迟 2-4 秒后显示AI回复
      const randomDelay = Math.floor(Math.random() * 2000) + 2000
      
      // 存储定时器ID，用于页面卸载时清理
      const imageReplyTimer = setTimeout(async () => {
        // 检查页面是否还存在
        if (!this.data) return
        
        // 标记图片回复正在处理中
        this.isImageReplyProcessing = true
        
        // 只有在没有正在输入状态时才显示（避免覆盖文字消息的输入状态）
        const shouldShowTyping = !this.data.isTyping
        if (shouldShowTyping) {
          this.setData({ isTyping: true })
        }
        
        try {
          // 调用图片回复话术API
          const res = await api.imageReply.getRandom()
          
          // 检查页面是否还存在
          if (!this.data) {
            this.isImageReplyProcessing = false
            return
          }
          
          // 只有当前是图片回复触发的isTyping时才关闭
          if (shouldShowTyping && !this.isProcessing) {
            this.setData({ isTyping: false })
          }
          
          if (res && res.success && res.data && res.data.content) {
            // 添加AI回复
            const aiMessage = {
              id: util.generateId(),
              text: res.data.content,
              isMe: false,
              time: util.formatTime(new Date(), 'HH:mm'),
              type: 'text'  // 标记为文字消息
            }
            
            this.setData({
              messages: [...this.data.messages, aiMessage]
            }, () => {
              this.scrollToBottom()
            })
          } else {
            // API返回失败，使用默认回复
            this.showDefaultImageReply()
          }
        } catch (err) {
          console.error('[chat-detail] 获取图片回复话术失败:', err)
          if (this.data) {
            // 只有当前是图片回复触发的isTyping时才关闭
            if (shouldShowTyping && !this.isProcessing) {
              this.setData({ isTyping: false })
            }
            // 使用默认回复
            this.showDefaultImageReply()
          }
        } finally {
          // 清除图片回复处理标记
          this.isImageReplyProcessing = false
        }
      }, randomDelay)
      
      // 保存定时器引用，用于清理
      if (!this.imageReplyTimers) {
        this.imageReplyTimers = []
      }
      this.imageReplyTimers.push(imageReplyTimer)
      
    } catch (err) {
      console.error('[chat-detail] 图片上传失败:', err)
      
      // 更新消息状态为失败
      const messages = this.data.messages.map(msg => {
        if (msg.id === newId) {
          return { ...msg, uploading: false, uploadFailed: true }
        }
        return msg
      })
      this.setData({ messages })
      
      util.showError('图片发送失败')
    }
  },

  /**
   * 显示默认图片回复（API失败时的兜底）
   */
  showDefaultImageReply() {
    const defaultReplies = [
      '哇，这张图片真好看！',
      '谢谢你分享这张图片给我~',
      '这张图片很有意思呢！',
      '收到你的图片啦，真棒！'
    ]
    const randomIndex = Math.floor(Math.random() * defaultReplies.length)
    
    const aiMessage = {
      id: util.generateId(),
      text: defaultReplies[randomIndex],
      isMe: false,
      time: util.formatTime(new Date(), 'HH:mm'),
      type: 'text'  // 标记为文字消息
    }
    
    this.setData({
      messages: [...this.data.messages, aiMessage]
    }, () => {
      this.scrollToBottom()
    })
  },

  /**
   * 预览图片
   */
  onPreviewImage(e) {
    const url = e.currentTarget.dataset.url
    const urls = this.data.messages
      .filter(msg => msg.type === 'image')
      .map(msg => msg.imageUrl)
    
    wx.previewImage({
      current: url,
      urls: urls
    })
  },

  /**
   * 发送礼物
   */
  onSendGift() {
    this.setData({ showMorePanel: false })
    
    // 加载礼物列表
    this.loadGiftList()
    this.setData({ showGiftPopup: true })
  },

  /**
   * 加载礼物列表
   */
  async loadGiftList() {
    // 模拟礼物数据
    const giftList = [
      { id: 1, name: '玫瑰花', price: 10, image: '/images/gift-rose.png' },
      { id: 2, name: '爱心', price: 20, image: '/images/gift-heart.png' },
      { id: 3, name: '蛋糕', price: 50, image: '/images/gift-cake.png' },
      { id: 4, name: '钻戒', price: 100, image: '/images/gift-ring.png' },
      { id: 5, name: '跑车', price: 500, image: '/images/gift-car.png' },
      { id: 6, name: '城堡', price: 1000, image: '/images/gift-castle.png' },
      { id: 7, name: '火箭', price: 2000, image: '/images/gift-rocket.png' },
      { id: 8, name: '皇冠', price: 5000, image: '/images/gift-crown.png' }
    ]
    
    this.setData({ giftList })
  },

  /**
   * 选择礼物
   */
  onSelectGift(e) {
    const gift = e.currentTarget.dataset.gift
    this.setData({ selectedGift: gift })
  },

  /**
   * 关闭礼物弹窗
   */
  onCloseGiftPopup() {
    this.setData({
      showGiftPopup: false,
      selectedGift: null
    })
  },

  /**
   * 确认发送礼物
   */
  async onConfirmSendGift() {
    const { selectedGift, userFlowers, character } = this.data
    
    if (!selectedGift) {
      util.showError('请选择礼物')
      return
    }
    
    if (userFlowers < selectedGift.price) {
      util.showError('花朵余额不足')
      // 跳转充值页面
      setTimeout(() => {
        wx.navigateTo({ url: '/pages/recharge/recharge' })
      }, 1500)
      return
    }
    
    // 发送礼物消息
    const newId = util.generateId()
    const giftMessage = {
      id: newId,
      type: 'gift',
      text: `送出了 ${selectedGift.name}`,
      giftInfo: selectedGift,
      isMe: true,
      time: util.formatTime(new Date(), 'HH:mm')
    }
    
    this.setData({
      messages: [...this.data.messages, giftMessage],
      showGiftPopup: false,
      selectedGift: null,
      userFlowers: userFlowers - selectedGift.price
    }, () => {
      this.scrollToBottom()
    })
    
    util.showSuccess('礼物已送出')
    
    // TODO: 调用后端API发送礼物
  },

  /**
   * 语音通话
   */
  onVoiceCall() {
    this.setData({ showMorePanel: false })
    
    wx.showModal({
      title: '语音通话',
      content: '语音通话功能即将上线，敬请期待~',
      showCancel: false,
      confirmText: '知道了'
    })
  },

  /**
   * 常用语
   */
  onQuickReply() {
    this.setData({ showMorePanel: false })
    
    wx.showActionSheet({
      itemList: this.data.quickReplies,
      success: (res) => {
        const selectedReply = this.data.quickReplies[res.tapIndex]
        this.setData({ inputText: selectedReply })
      }
    })
  },

  /**
   * 约时间
   */
  onScheduleTime() {
    this.setData({ showMorePanel: false })
    
    wx.showModal({
      title: '约时间',
      content: '预约功能即将上线，敬请期待~',
      showCancel: false,
      confirmText: '知道了'
    })
  },

  /**
   * 抢红包
   */
  onRedPacket() {
    this.setData({ showMorePanel: false })
    
    wx.showModal({
      title: '抢红包',
      content: '红包功能即将上线，敬请期待~',
      showCancel: false,
      confirmText: '知道了'
    })
  },

  /**
   * 测结果
   */
  onTestResult() {
    this.setData({ showMorePanel: false })
    
    // 跳转到测试结果页面
    wx.showModal({
      title: '测结果',
      content: '心理测试功能即将上线，敬请期待~',
      showCancel: false,
      confirmText: '知道了'
    })
  },

  /**
   * 语音录制相关方法
   */
  onVoiceTouchStart(e) {
    this.touchStartY = e.touches[0].clientY
    this.setData({
      isRecording: true,
      voiceCancelHint: false,
      recordingDuration: 0
    })
    
    // 开始录音
    const recorderManager = wx.getRecorderManager()
    recorderManager.start({
      duration: 60000,
      format: 'mp3'
    })
    
    this.recorderManager = recorderManager
    
    // 录音时长计时
    this.recordingTimer = setInterval(() => {
      this.setData({
        recordingDuration: this.data.recordingDuration + 1
      })
    }, 1000)
  },

  onVoiceTouchMove(e) {
    const moveY = e.touches[0].clientY
    const diff = this.touchStartY - moveY
    
    // 上滑超过50px显示取消提示
    this.setData({
      voiceCancelHint: diff > 50
    })
  },

  onVoiceTouchEnd() {
    clearInterval(this.recordingTimer)
    
    const { voiceCancelHint, recordingDuration, characterId, character, isUnlocked, remainingCount } = this.data
    
    this.setData({ isRecording: false })
    
    if (this.recorderManager) {
      this.recorderManager.stop()
      
      if (voiceCancelHint) {
        // 取消发送
        util.showToast('已取消')
        return
      }
      
      if (recordingDuration < 1) {
        util.showError('录音时间太短')
        return
      }
      
      this.recorderManager.onStop(async (res) => {
        console.log('[chat-detail] 录音完成:', res.tempFilePath, '时长:', recordingDuration)
        
        // 先显示语音消息（带识别中状态）
        const newId = util.generateId()
        const voiceMessage = {
          id: newId,
          type: 'voice',
          audioUrl: res.tempFilePath,
          duration: recordingDuration,
          isMe: true,
          time: util.formatTime(new Date(), 'HH:mm'),
          recognizing: true,  // 识别中状态
          recognizedText: ''  // 识别出的文字
        }
        
        this.setData({
          messages: [...this.data.messages, voiceMessage]
        }, () => {
          this.scrollToBottom()
        })
        
        // 进行语音识别
        try {
          wx.showLoading({ title: '语音识别中...' })
          
          // 读取音频文件并转换为base64
          const fs = wx.getFileSystemManager()
          const audioData = fs.readFileSync(res.tempFilePath)
          const audioBase64 = wx.arrayBufferToBase64(audioData)
          
          // 调用语音识别API
          const recognizeRes = await api.speech.recognize({
            audio: audioBase64,
            format: 'mp3'
          })
          
          wx.hideLoading()
          
          let recognizedText = ''
          if (recognizeRes.success && recognizeRes.data && recognizeRes.data.text) {
            recognizedText = recognizeRes.data.text
            console.log('[chat-detail] 语音识别结果:', recognizedText)
          } else {
            // 识别失败，使用默认文字
            recognizedText = '[语音消息]'
            console.log('[chat-detail] 语音识别失败，使用默认文字')
          }
          
          // 更新语音消息的识别状态
          const messages = this.data.messages.map(msg => {
            if (msg.id === newId) {
              return { ...msg, recognizing: false, recognizedText }
            }
            return msg
          })
          this.setData({ messages })
          
          // 如果识别出了有效文字，发送给AI
          if (recognizedText && recognizedText !== '[语音消息]') {
            // 检查聊天权限
            const canChatByFreeTime = !!(this.data.freeTime && this.data.freeTime.isActive)
            const canChatByVip = !!this.data.isVip
            
            if (!isUnlocked && !canChatByVip && !canChatByFreeTime) {
              console.log('[chat-detail] 语音消息无聊天权限', { isUnlocked, isVip, canChatByFreeTime })
              this.setData({ showUnlockPopup: true })
              return
            }
            
            // 将识别出的文字加入待处理队列
            this.pendingMessages.push(recognizedText)
            
            // 如果没有正在等待的定时器，启动延迟处理
            if (!this.messageTimer) {
              this.startMessageTimer(characterId, this.data.conversationId, character, isUnlocked, remainingCount)
            }
          }
          
        } catch (err) {
          wx.hideLoading()
          console.error('[chat-detail] 语音识别失败:', err)
          
          // 更新消息状态
          const messages = this.data.messages.map(msg => {
            if (msg.id === newId) {
              return { ...msg, recognizing: false, recognizedText: '[语音消息]' }
            }
            return msg
          })
          this.setData({ messages })
          
          util.showError('语音识别失败')
        }
      })
    }
  },

  onVoiceTouchCancel() {
    clearInterval(this.recordingTimer)
    this.setData({ isRecording: false })
    
    if (this.recorderManager) {
      this.recorderManager.stop()
    }
  },

  /**
   * 消息长按操作
   */
  onMessageLongPress(e) {
    const item = e.currentTarget.dataset.item
    
    wx.showActionSheet({
      itemList: ['复制', '删除'],
      success: (res) => {
        if (res.tapIndex === 0) {
          // 复制
          wx.setClipboardData({
            data: item.text,
            success: () => {
              util.showSuccess('已复制')
            }
          })
        } else if (res.tapIndex === 1) {
          // 删除
          const messages = this.data.messages.filter(msg => msg.id !== item.id)
          this.setData({ messages })
        }
      }
    })
  },

  /**
   * 阻止事件冒泡
   */
  preventBubble() {
    return
  },

  /**
   * 阻止触摸穿透
   */
  preventTouchMove() {
    return false
  }
})
