/**
 * 即时通讯状态 Store
 *
 * 管理会话列表、消息、未读数，提供：
 * - 会话列表 CRUD + 排序（置顶优先 → 最后消息时间降序）
 * - 当前会话消息管理（发送/接收/撤回/历史加载）
 * - 全局未读数（TabBar badge 显示）
 * - WebSocket 事件监听（im.message.new / im.message.recalled / im.offline.sync）
 * - 三态消息确认（sending → sent → failed）
 *
 * 对应后端 API：/api/v1/im/*
 * 对应 WS 事件：im.message.send / im.message.recall / im.conversation.read / im.typing
 */

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import imApi from '@/api/im'
import wsService from '@/services/websocket'
import { useUserStore } from '@/store/user'

export const useChatStore = defineStore('chat', () => {
  /** 会话列表 */
  const conversationList = ref([])

  /** 当前聊天会话 ID */
  const currentConversationId = ref(null)

  /** 消息缓存 conversationId -> Message[] */
  const messagesMap = ref({})

  /** 发送中消息队列 clientMsgId -> { status, ... } */
  const pendingMessages = ref({})

  /** 全局未读消息总数（用于 TabBar badge） */
  const totalUnread = ref(0)

  /** 正在输入状态 conversationId -> boolean */
  const typingMap = ref({})

  /** 是否还有更多历史消息 conversationId -> boolean */
  const hasMoreMap = ref({})

  /** 单聊已读状态：conversationId -> lastReadMsgId（对方已读到的消息 ID） */
  const readStatusMap = ref({})

  /** 群聊已读计数：messageId -> readCount */
  const groupReadCountMap = ref({})

  /**
   * 语音"已播放"状态：messageId -> true
   *
   * 说明：区别于通用"已读"（基于滚动可见）；此状态记录用户是否"真的点击播放过"对方发来的语音。
   * - 仅对"对方发来的语音"有意义（自己发的无需标记）
   * - 本地持久化（按 userId 隔离），换设备会重新显示红点（与微信行为一致）
   * - 不同步到服务端：属于私人视图态，不影响对方
   */
  const voicePlayedMap = ref({})
  /** 已加载 voicePlayed 的 userId，避免重复加载 */
  let _voicePlayedLoadedUserId = null

  /** 生成 voicePlayed 的存储 key（按 userId 隔离） */
  const _voicePlayedStorageKey = (userId) => `echo:voice-played:${userId}`

  /**
   * 从本地存储加载指定用户的语音已播放状态
   * 幂等：同一 userId 只会加载一次；切换用户时自动重新加载
   */
  const loadVoicePlayedState = (userId) => {
    if (!userId) return
    if (_voicePlayedLoadedUserId === userId) return
    try {
      const raw = uni.getStorageSync(_voicePlayedStorageKey(userId))
      voicePlayedMap.value = raw && typeof raw === 'object' ? { ...raw } : {}
    } catch (_) {
      voicePlayedMap.value = {}
    }
    _voicePlayedLoadedUserId = userId
  }

  /**
   * 标记某条语音消息为"已播放"，并持久化到本地存储
   * @param {number|string} messageId
   */
  const markVoicePlayed = (messageId) => {
    if (!messageId) return
    const key = String(messageId)
    if (voicePlayedMap.value[key]) return
    voicePlayedMap.value[key] = true
    try {
      const userStore = useUserStore()
      const userId = userStore.userInfo?.id
      if (userId) {
        uni.setStorageSync(_voicePlayedStorageKey(userId), { ...voicePlayedMap.value })
      }
    } catch (_) { /* ignore storage errors */ }
  }

  /**
   * 查询某条语音是否已播放
   * @param {number|string} messageId
   * @returns {boolean}
   */
  const isVoicePlayed = (messageId) => {
    if (!messageId) return false
    return !!voicePlayedMap.value[String(messageId)]
  }

  /** 重置语音已播放状态（登出时调用） */
  const resetVoicePlayedState = () => {
    voicePlayedMap.value = {}
    _voicePlayedLoadedUserId = null
  }

  // ==================== Computed ====================

  /** 当前会话的消息列表（conversationId 为 null 时取 pending 队列） */
  const currentMessages = computed(() => {
    if (!currentConversationId.value) {
      return messagesMap.value['pending'] || []
    }
    return messagesMap.value[currentConversationId.value] || []
  })

  /** 按排序规则的会话列表（置顶优先 → 最后消息时间降序） */
  const sortedConversations = computed(() => {
    return [...conversationList.value].sort((a, b) => {
      if (a.is_pinned !== b.is_pinned) return a.is_pinned ? -1 : 1
      const timeA = a.last_msg_time ? new Date(a.last_msg_time).getTime() : 0
      const timeB = b.last_msg_time ? new Date(b.last_msg_time).getTime() : 0
      return timeB - timeA
    })
  })

  // ==================== Actions ====================

  /** 加载会话列表 */
  const fetchConversations = async () => {
    const res = await imApi.getConversations()
    conversationList.value = (res.data && res.data.list) || []
    _recalcTotalUnread()
    return conversationList.value
  }

  /** 加载全局未读数（从后端获取，同步 Redis 数据） */
  const fetchTotalUnread = async () => {
    const res = await imApi.getTotalUnread()
    if (res.data) {
      totalUnread.value = res.data.total_unread || 0
    }
  }

  /**
   * 发送消息（统一入口，支持文本 + 富媒体）
   * @param {Object} params - { conversationId, targetUserId, content, type, extra, at_user_ids }
   * @returns {string} clientMsgId
   */
  const sendMessage = (params) => {
    const clientMsgId = _generateClientMsgId()
    const { conversationId, targetUserId, content = '', type = 1, extra = '', at_user_ids } = params
    const userStore = useUserStore()

    const tempMsg = {
      id: 0,
      conversation_id: conversationId || 0,
      sender_id: Number(userStore.userInfo?.id) || 0,
      type,
      content,
      extra: extra || undefined,
      status: 1,
      client_msg_id: clientMsgId,
      created_at: new Date().toISOString().replace('T', ' ').substring(0, 19),
      _sending: true
    }

    const displayConvId = conversationId || 'pending'
    _appendMessage(displayConvId, tempMsg)

    pendingMessages.value[clientMsgId] = { status: 'sending', tempMsg }

    const payload = {
      conversation_id: conversationId || 0,
      target_user_id: targetUserId || 0,
      type,
      content,
      extra,
      client_msg_id: clientMsgId
    }
    if (at_user_ids && at_user_ids.length > 0) {
      payload.at_user_ids = at_user_ids
    }

    const seq = wsService.send('im.message.send', payload)

    if (seq === -1) {
      pendingMessages.value[clientMsgId].status = 'failed'
      tempMsg._sending = false
      tempMsg._failed = true
    }

    return clientMsgId
  }

  /**
   * 发送图片消息
   * @param {Object} params - { conversationId, targetUserId, images }
   * images: [{ url, thumbnail_url, width, height, size, file_name }]
   */
  const sendImageMessage = (params) => {
    const { conversationId, targetUserId, images } = params
    const extra = JSON.stringify({ images })
    return sendMessage({
      conversationId,
      targetUserId,
      content: '',
      type: 2,
      extra
    })
  }

  /**
   * 发送语音消息
   * @param {Object} params - { conversationId, targetUserId, voice }
   * voice: { url, duration, size, file_name }
   */
  const sendVoiceMessage = (params) => {
    const { conversationId, targetUserId, voice } = params
    const extra = JSON.stringify({ voice })
    return sendMessage({
      conversationId,
      targetUserId,
      content: '',
      type: 3,
      extra
    })
  }

  /**
   * 发送文件消息
   * @param {Object} params - { conversationId, targetUserId, file }
   * file: { url, file_name, size, mime_type, ext }
   */
  const sendFileMessage = (params) => {
    const { conversationId, targetUserId, file } = params
    const extra = JSON.stringify({ file })
    return sendMessage({
      conversationId,
      targetUserId,
      content: '',
      type: 5,
      extra
    })
  }

  /** 撤回消息 */
  const recallMessage = (messageId) => {
    wsService.send('im.message.recall', { message_id: messageId })
  }

  /** 标记会话已读 */
  const markRead = (conversationId) => {
    const conv = conversationList.value.find(c => c.id === conversationId)
    if (conv && conv.unread_count > 0) {
      totalUnread.value = Math.max(0, totalUnread.value - conv.unread_count)
      conv.unread_count = 0
    }
    wsService.send('im.conversation.read', { conversation_id: conversationId })
  }

  /** 发送正在输入通知 */
  const sendTyping = (conversationId) => {
    wsService.send('im.typing', { conversation_id: conversationId })
  }

  /** 加载历史消息 */
  const loadHistoryMessages = async (conversationId) => {
    if (!conversationId) return
    const messages = messagesMap.value[conversationId] || []
    const beforeId = messages.length > 0 ? messages[0].id : 0
    // 仅在「首次加载（本地还没缓存任何消息）」时回填已读状态；
    // 滚动加载更多历史消息时不覆盖，避免把已通过 WS 增量到达的最新状态冲回旧值
    const isInitialLoad = messages.length === 0

    const res = await imApi.getHistoryMessages(conversationId, beforeId)
    if (res.data) {
      const list = res.data.list || []
      hasMoreMap.value[conversationId] = res.data.has_more

      if (list.length > 0) {
        const existing = messagesMap.value[conversationId] || []
        messagesMap.value[conversationId] = [...list.reverse(), ...existing]
      }

      // ========== 已读状态回填（修复页面刷新后已读标签变未读的 Bug） ==========
      if (isInitialLoad) {
        // 单聊：对方 last_read_msg_id → 写入 readStatusMap
        const peerLastRead = Number(res.data.peer_last_read_msg_id) || 0
        if (peerLastRead > 0) {
          readStatusMap.value[conversationId] = peerLastRead
        }
        // 群聊：自己发送消息的 read_count → 合并到 groupReadCountMap
        const rcMap = res.data.read_count_map
        if (rcMap && typeof rcMap === 'object') {
          Object.keys(rcMap).forEach(mid => {
            const count = Number(rcMap[mid]) || 0
            if (count > 0) groupReadCountMap.value[mid] = count
          })
        }
      }
    }
  }

  /** 置顶/取消置顶 */
  const pinConversation = async (conversationId, isPinned) => {
    await imApi.pinConversation(conversationId, isPinned)
    const conv = conversationList.value.find(c => c.id === conversationId)
    if (conv) conv.is_pinned = isPinned
  }

  /** 删除会话 */
  const deleteConversation = async (conversationId) => {
    await imApi.deleteConversation(conversationId)
    conversationList.value = conversationList.value.filter(c => c.id !== conversationId)
    if (currentConversationId.value === conversationId) {
      currentConversationId.value = null
    }
    _recalcTotalUnread()
  }

  /** 清空聊天记录 */
  const clearHistory = async (conversationId) => {
    await imApi.clearHistory(conversationId)
    messagesMap.value[conversationId] = []
    const conv = conversationList.value.find(c => c.id === conversationId)
    if (conv) {
      conv.last_msg_content = ''
      conv.last_msg_time = ''
    }
  }

  /** 设置当前会话 */
  const setCurrentConversation = (conversationId) => {
    currentConversationId.value = conversationId
    if (conversationId) {
      markRead(conversationId)
    }
  }

  // ==================== WS 事件监听 ====================

  let _wsInitialized = false

  /** 初始化 WebSocket 事件监听（幂等） */
  const initWsListeners = () => {
    // 懒加载语音播放态（按当前用户隔离，每次调用都会检查 userId 是否变更）
    try {
      const userStore = useUserStore()
      const uid = userStore.userInfo?.id
      if (uid) loadVoicePlayedState(uid)
    } catch (_) { /* ignore */ }

    if (_wsInitialized) return
    _wsInitialized = true

    wsService.on('im.message.new', _onNewMessage)
    wsService.on('im.message.recalled', _onMessageRecalled)
    wsService.on('im.message.send.ack', _onSendACK)
    wsService.on('im.offline.sync', _onOfflineSync)
    wsService.on('im.typing', _onTyping)
    wsService.on('im.message.read.ack', _onReadACK)
    wsService.on('im.message.read.count', _onReadCount)
  }

  /** 收到新消息推送 */
  const _onNewMessage = (msg) => {
    if (!msg || !msg.data) return
    const data = msg.data

    _appendMessage(data.conversation_id, {
      id: data.id,
      conversation_id: data.conversation_id,
      sender_id: data.sender_id,
      type: data.type,
      content: data.content,
      extra: data.extra || undefined,
      status: 1,
      client_msg_id: data.client_msg_id || '',
      created_at: data.created_at
    })

    const existingConv = conversationList.value.find(c => c.id === data.conversation_id)
    if (existingConv) {
      const preview = _getMessagePreview(data.type, data.content, data.extra)
      _updateConversationPreview(data.conversation_id, preview, data.created_at, data.sender_id)
    } else {
      fetchConversations()
    }

    if (data.conversation_id !== currentConversationId.value) {
      if (existingConv) {
        existingConv.unread_count = (existingConv.unread_count || 0) + 1
      }
      totalUnread.value++
    }
    // 注意：当前会话的 markRead 由聊天页面根据滚动位置决定
    // （仅当用户视图贴近底部、能看到新消息时才触发），避免"视图未看到但已读"的 UX 错位
  }

  /** 消息撤回推送 */
  const _onMessageRecalled = (msg) => {
    if (!msg || !msg.data) return
    const { message_id, conversation_id } = msg.data
    const messages = messagesMap.value[conversation_id]
    if (messages) {
      const idx = messages.findIndex(m => m.id === message_id)
      if (idx !== -1) {
        messages[idx].status = 2
        messages[idx].content = '[消息已撤回]'
      }
    }
  }

  /** 发送消息 ACK 响应 */
  const _onSendACK = (msg) => {
    if (!msg) return
    const { code, data } = msg
    if (!data || !data.client_msg_id) return

    const pending = pendingMessages.value[data.client_msg_id]
    if (!pending) return

    if (code === 0) {
      pending.status = 'sent'
      const convId = data.conversation_id

      if (pending.tempMsg && pending.tempMsg.conversation_id === 0 && convId) {
        currentConversationId.value = convId
        const pendingMsgs = messagesMap.value['pending'] || []
        if (pendingMsgs.length > 0) {
          if (!messagesMap.value[convId]) messagesMap.value[convId] = []
          messagesMap.value[convId].push(...pendingMsgs)
          delete messagesMap.value['pending']
        }
      }

      const messages = messagesMap.value[convId]
      if (messages) {
        const idx = messages.findIndex(m => m.client_msg_id === data.client_msg_id)
        if (idx !== -1) {
          messages[idx] = { ...messages[idx], ...data, _sending: false }
        } else {
          _appendMessage(convId, { ...data, _sending: false })
        }
      } else {
        _appendMessage(convId, { ...data, _sending: false })
      }
      _updateConversationPreview(convId, data.content, data.created_at, data.sender_id)
    } else {
      pending.status = 'failed'
      if (pending.tempMsg) {
        pending.tempMsg._sending = false
        pending.tempMsg._failed = true
      }
    }
    delete pendingMessages.value[data.client_msg_id]
  }

  /** 离线消息同步推送 */
  const _onOfflineSync = (msg) => {
    if (!msg || !msg.data) return
    const { total_unread } = msg.data
    totalUnread.value = total_unread || 0
    fetchConversations()
  }

  /** 单聊已读确认（对方已读到某条消息） */
  const _onReadACK = (msg) => {
    if (!msg || !msg.data) return
    const { conversation_id, last_read_msg_id } = msg.data
    readStatusMap.value[conversation_id] = last_read_msg_id
  }

  /** 群聊已读计数更新 */
  const _onReadCount = (msg) => {
    if (!msg || !msg.data) return
    const { message_id, read_count } = msg.data
    groupReadCountMap.value[message_id] = read_count
  }

  /** 群聊标记已读（发送 WS 事件） */
  const markGroupRead = (conversationId, messageIds) => {
    if (!messageIds || messageIds.length === 0) return
    wsService.send('im.group.read', {
      conversation_id: conversationId,
      message_ids: messageIds
    })
  }

  /** 正在输入通知 */
  const _onTyping = (msg) => {
    if (!msg || !msg.data) return
    const { conversation_id } = msg.data
    typingMap.value[conversation_id] = true
    setTimeout(() => {
      typingMap.value[conversation_id] = false
    }, 3000)
  }

  // ==================== 内部工具方法 ====================

  /**
   * 追加消息到缓存
   *
   * 去重 + 临时消息合并（2026-04-23 加固）：
   *   - 若已存在同 id 的消息：真重复，直接丢弃
   *   - 若匹配到相同 client_msg_id 的本地临时消息（_sending=true）：
   *     就地把服务端权威字段（id/created_at/...）合并进去，并清除
   *     _sending / _failed 标记；这样即使 ACK 丢失、但广播帧到了，
   *     也能把发送方的"转圈"状态变成已入库可读/未读态，避免 UX 卡死
   *   - 否则追加一条新消息
   */
  const _appendMessage = (conversationId, message) => {
    if (!messagesMap.value[conversationId]) {
      messagesMap.value[conversationId] = []
    }
    const list = messagesMap.value[conversationId]

    if (message.id) {
      const idIdx = list.findIndex(m => m.id === message.id)
      if (idIdx !== -1) return
    }

    if (message.client_msg_id) {
      const tmpIdx = list.findIndex(m => m.client_msg_id === message.client_msg_id)
      if (tmpIdx !== -1) {
        list[tmpIdx] = {
          ...list[tmpIdx],
          ...message,
          _sending: false,
          _failed: false
        }
        return
      }
    }

    list.push(message)
  }

  /** 更新会话列表中的预览信息 */
  const _updateConversationPreview = (conversationId, content, time, senderId) => {
    const conv = conversationList.value.find(c => c.id === conversationId)
    if (conv) {
      conv.last_msg_content = content
      conv.last_msg_time = time
      conv.last_msg_sender_id = senderId
    }
  }

  /** 根据会话列表重新计算总未读数 */
  const _recalcTotalUnread = () => {
    totalUnread.value = conversationList.value.reduce((sum, c) => sum + (c.unread_count || 0), 0)
  }

  /** 根据消息类型生成会话预览文案 */
  const _getMessagePreview = (type, content, extra) => {
    switch (type) {
      case 2: {
        let count = 1
        try {
          const parsed = typeof extra === 'string' ? JSON.parse(extra) : extra
          if (parsed?.images?.length > 1) count = parsed.images.length
        } catch {}
        return count > 1 ? `[图片x${count}]` : '[图片]'
      }
      case 3: {
        let dur = 0
        try {
          const parsed = typeof extra === 'string' ? JSON.parse(extra) : extra
          dur = parsed?.voice?.duration || 0
        } catch {}
        return dur > 0 ? `[语音 ${dur}"]` : '[语音]'
      }
      case 5: {
        let name = ''
        try {
          const parsed = typeof extra === 'string' ? JSON.parse(extra) : extra
          name = parsed?.file?.file_name || ''
        } catch {}
        return name ? `[文件] ${name}` : '[文件]'
      }
      default:
        return content || ''
    }
  }

  /** 生成客户端消息唯一 ID */
  const _generateClientMsgId = () => {
    return `${Date.now()}-${Math.random().toString(36).substring(2, 10)}`
  }

  return {
    conversationList,
    currentConversationId,
    messagesMap,
    pendingMessages,
    totalUnread,
    typingMap,
    hasMoreMap,
    readStatusMap,
    groupReadCountMap,
    voicePlayedMap,
    currentMessages,
    sortedConversations,
    fetchConversations,
    fetchTotalUnread,
    sendMessage,
    sendImageMessage,
    sendVoiceMessage,
    sendFileMessage,
    recallMessage,
    markRead,
    markGroupRead,
    sendTyping,
    loadHistoryMessages,
    pinConversation,
    deleteConversation,
    clearHistory,
    setCurrentConversation,
    initWsListeners,
    markVoicePlayed,
    isVoicePlayed,
    loadVoicePlayedState,
    resetVoicePlayedState
  }
})
