import type { Friend, FriendLite, FriendRequest } from '../types'

import type { ImFriendApi } from '#/api/im/friend'
import type { ImFriendRequestApi } from '#/api/im/friend/request'

import { CommonStatusEnum } from '#/packages/constants/src'

import { acceptHMRUpdate, defineStore } from 'pinia'

import { blockFriend as apiBlockFriend, deleteFriend as apiDeleteFriend, getFriend as apiGetFriend, getMyFriendList as apiGetMyFriendList, pullMyFriendList as apiPullMyFriendList, unblockFriend as apiUnblockFriend, updateFriend as apiUpdateFriend } from '#/api/im/friend'
import { agreeFriendRequest as apiAgreeFriendRequest, applyFriendRequest as apiApplyFriendRequest, getMyFriendRequest as apiGetMyFriendRequest, getMyFriendRequestList as apiGetMyFriendRequestList, pullMyFriendRequestList as apiPullMyFriendRequestList, refuseFriendRequest as apiRefuseFriendRequest } from '#/api/im/friend/request'
import { getCurrentUserId } from '#/views/im/utils/auth'

import { FRIEND_REQUEST_PAGE_SIZE } from '../../utils/config'
import { ImConversationType, ImFriendRequestHandleResult } from '../../utils/constants'
import { getDb, StorageKeys } from '../../utils/db'
import { runIncrementalPull } from '../../utils/pull'
import { getFriendDisplayName } from '../../utils/user'
import { useConversationStore } from './conversationStore'

type PendingRequest = { epoch: number; promise: Promise<void>; userId: number; }

/** 当前正在进行的好友列表拉取；多 dispatcher 同时触发时复用同一 Promise，避免雪崩重拉 */
let pendingFetchFriends: null | PendingRequest = null
/** 当前正在进行的好友申请列表拉取；多端连续多条申请到达时复用同一 Promise，避免雪崩重拉 */
let pendingFetchRequests: null | PendingRequest = null
/** 当前正在进行的「加载更多申请」请求 */
let pendingLoadMoreRequests: null | PendingRequest = null
/** 当前正在进行的好友详情请求 */
const pendingFetchFriendInfos = new Map<string, Promise<void>>()

/** clear() 时递增；旧账号那次还没返回的请求 resolve 后比对一致才写 store，防跨账号数据泄漏 */
let storeEpoch = 0

/** 构建好友详情请求去重 key */
function getPendingFriendInfoKey(userId: number, friendUserId: number): string {
  return `${userId}:${friendUserId}`
}

/** 好友通知 payload（对齐后端 BaseFriendNotification + 子类裁减后的字段） */
export interface FriendNotificationPayload {
  operatorUserId: number
  friendUserId: number
  // FRIEND_REQUEST_* 系列：申请记录的核心字段（避免 payload 携带完整 DO）
  requestId?: number
  applyContent?: string
  handleContent?: string
  addSource?: number
  // FRIEND_REQUEST_RECEIVED：申请方聚合字段，供前端直推 push 进列表，无需回拉
  fromNickname?: string
  fromAvatar?: string
  // FRIEND_UPDATE：单边属性变更
  displayName?: string
  silent?: boolean
  pinned?: boolean
  // FRIEND_DELETE：是否级联清理本端相关数据（如私聊会话）
  clear?: boolean
}

/**
 * IM 好友 Store
 *
 * 负责：
 * - 拉取 / 缓存当前登录用户的好友列表 + 申请列表
 * - 申请-审批流程（apply / agree / refuse）+ 备注 / 免打扰 / 联系人置顶 / 拉黑
 * - 接收 WebSocket 1201-1210 段位通知，按事件分发到 friendStore 内部各 dispatcher
 */
export const useFriendStore = defineStore('imFriendStore', {
  state: () => ({
    friends: [] as Friend[],
    // 仅 fetchFriendList 成功后置位；loadFriendData（IDB）不置位，否则后台 SWR 刷新会被缓存命中跳过
    loaded: false,
    /** 我相关的好友申请列表（含我发起的 + 别人加我的；后端按 id 倒序游标分页） */
    friendRequests: [] as FriendRequest[],
    /** 是否还有更早的申请记录可加载；返回不满 page size 即置 false */
    hasMoreFriendRequests: true
  }),

  getters: {
    /**
     * friendUserId → Friend 的 O(1) 索引；从 friends 数组派生，Pinia 在 friends 变化时自动重算
     *
     * 消息渲染需要按 senderId 反查发送人头像 / 备注，每条消息渲染都会 getFriend；
     * 直接 find 时 N 条消息 × M 好友 = O(N×M)；建索引后单次读 O(1)，重建只在写好友（fetchFriendList / upsertFriend 等）时发生
     */
    getFriendMap: (state): Map<number, Friend> => {
      const map = new Map<number, Friend>()
      for (const friend of state.friends) {
        map.set(friend.friendUserId, friend)
      }
      return map
    },
    /** 按 friendUserId 找好友（含已软删的 DISABLE 记录，调用方自行判定） */
    getFriend(): (friendUserId: number) => Friend | undefined {
      return (friendUserId: number) => this.getFriendMap.get(friendUserId)
    },
    /** 当前生效的好友列表（过滤掉 DISABLE 软删记录） */
    getActiveFriendList: (state): Friend[] => {
      return state.friends.filter((friend) => friend.status !== CommonStatusEnum.DISABLE)
    },
    /** 当前生效好友的 Lite 视图（PickerPanel / 选人弹窗共用，自带拼音字段供分桶 / 搜索） */
    getActiveFriendLiteList(): FriendLite[] {
      return this.getActiveFriendList.map((friend: Friend) => ({
        id: friend.friendUserId,
        nickname: friend.nickname,
        nicknamePinyin: friend.nicknamePinyin,
        avatar: friend.avatar,
        displayName: friend.displayName,
        displayNamePinyin: friend.displayNamePinyin
      }))
    },
    /** 判断对方是否是当前用户的有效好友（存在 + 非 DISABLE） */
    isActiveFriend() {
      return (friendUserId: number): boolean => {
        const entry = this.getFriend(friendUserId)
        return !!entry && entry.status !== CommonStatusEnum.DISABLE
      }
    },
    /** 我的黑名单（blocked=true 且 ENABLE） */
    getBlockedFriendList: (state): Friend[] => {
      return state.friends.filter(
        (friend) => friend.status !== CommonStatusEnum.DISABLE && friend.blocked === true
      )
    },
    /** 未处理申请数（接收方=我）—— 实时派生，「新的朋友」红点用 */
    getUnhandledRequestCount: (state): number => {
      const currentUserId = getCurrentUserId()
      return state.friendRequests.filter(
        (request) =>
          request.handleResult === ImFriendRequestHandleResult.UNHANDLED &&
          request.toUserId === currentUserId
      ).length
    }
  },

  actions: {
    // ==================== 本地缓存 ====================

    /** 从 IndexedDB 恢复好友和好友申请 */
    async loadFriendData(): Promise<boolean> {
      try {
        const [friends, friendRequests] = await Promise.all([
          getDb().getAll<Friend>('friends'),
          getDb().getAll<FriendRequest>('friendRequests')
        ])
        if (friends.length > 0) {
          this.friends = friends
        }
        if (friendRequests.length > 0) {
          this.friendRequests = friendRequests.toSorted(
            (requestA, requestB) => requestB.id - requestA.id
          )
          this.hasMoreFriendRequests = friendRequests.length >= FRIEND_REQUEST_PAGE_SIZE
        }
        return friends.length > 0
      } catch (error) {
        console.warn('[IM friendStore] 本地好友缓存读取失败', error)
        return false
      }
    },

    /** 保存好友列表 */
    saveFriendList(): void {
      void getDb()
        .transaction(['friends'], 'readwrite', async (tx) => {
          const db = getDb()
          await db.clearStore('friends', tx)
          for (const friend of this.friends) {
            if (friend.id) {
              await db.put('friends', friend, tx)
            }
          }
        })
        .catch((error) => console.warn('[IM friendStore] 本地好友缓存写入失败', error))
    },

    /** 保存单个好友 */
    async saveFriendRecord(friend: Friend | undefined): Promise<void> {
      if (!friend?.id) {
        return
      }
      await getDb().put('friends', friend)
    },

    /** 保存单个好友 */
    saveFriend(friend: Friend | undefined): void {
      void this.saveFriendRecord(friend).catch((error) =>
        console.warn('[IM friendStore] 本地好友写入失败', error)
      )
    },

    /** 保存好友申请列表 */
    saveFriendRequestList(): void {
      void getDb()
        .transaction(['friendRequests'], 'readwrite', async (tx) => {
          const db = getDb()
          await db.clearStore('friendRequests', tx)
          for (const request of this.friendRequests) {
            await db.put('friendRequests', request, tx)
          }
        })
        .catch((error) => console.warn('[IM friendStore] 本地好友申请缓存写入失败', error))
    },

    /** 保存单条好友申请 */
    async saveFriendRequestRecord(request: FriendRequest | undefined): Promise<void> {
      if (!request) {
        return
      }
      await getDb().put('friendRequests', request)
    },

    /** 保存单条好友申请 */
    saveFriendRequest(request: FriendRequest | undefined): void {
      void this.saveFriendRequestRecord(request).catch((error) =>
        console.warn('[IM friendStore] 本地好友申请写入失败', error)
      )
    },

    // ==================== 远端拉取 ====================

    /** 从后端拉取并覆盖本地列表（含 DISABLE 历史好友给已删对话兜底）；只同步 ENABLE 的会话信息，DISABLE 的不动 —— cascade 清会话由 WS dispatcher 按 payload.clear 处理，避免 fetchFriendList 覆盖用户「不清空聊天记录」的选择 */
    async fetchFriendList(force = false) {
      if (this.loaded && !force) {
        return
      }
      // 快照 epoch；clear() 之后到 .then 之间触发的 epoch++ 表示账号已切，旧结果不能写入新 store
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      if (
        pendingFetchFriends?.epoch === requestEpoch &&
        pendingFetchFriends.userId === requestUserId
      ) {
        return pendingFetchFriends.promise
      }
      const promise = apiGetMyFriendList()
        .then((list) => {
          if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
            return
          }
          this.friends = (list || []).map((friend) => convertFriend(friend))
          this.loaded = true
          const conversationStore = useConversationStore()
          for (const friend of this.friends) {
            if (friend.status === CommonStatusEnum.DISABLE) {
              continue
            }
            conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, {
              name: getFriendDisplayName(friend),
              avatar: friend.avatar,
              silent: friend.silent
            })
          }
          this.saveFriendList()
        })
        .finally(() => {
          if (
            pendingFetchFriends?.epoch === requestEpoch &&
            pendingFetchFriends.userId === requestUserId
          ) {
            pendingFetchFriends = null
          }
        })
      pendingFetchFriends = { epoch: requestEpoch, userId: requestUserId, promise }
      return promise
    },

    /**
     * 增量拉取好友变更并合并：进入 IM 首屏主数据恢复（空游标 = 首次全量增量拉）+ 重连 / 离线补偿
     *
     * 含已删除好友，按 status 走 upsert
     */
    async pullFriends() {
      // 快照 epoch；账号在拉取途中切换（clear() → epoch++）时丢弃旧账号那几页结果，防跨账号数据泄漏
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      const isActive = () => requestEpoch === storeEpoch && getCurrentUserId() === requestUserId
      await runIncrementalPull(
        StorageKeys.settings.friendPullCursor,
        apiPullMyFriendList,
        async (records) => {
          if (!isActive()) {
            return false
          }
          await Promise.all(records.map((vo) => this.upsertFriendForPull(convertFriend(vo))))
          return true
        },
        isActive
      )
      // 置 loaded，供通讯录页 fetchFriendList(force=false) 复用缓存而非重复全量拉
      if (isActive()) {
        this.loaded = true
      }
    },

    /** 按 friendUserId 获取详情并合并到本地（保证 nickname / avatar 最新） */
    async fetchFriendInfo(friendUserId: number) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      if (!requestUserId) {
        return
      }
      const key = getPendingFriendInfoKey(requestUserId, friendUserId)
      const inflight = pendingFetchFriendInfos.get(key)
      if (inflight) {
        return inflight
      }
      const promise = (async () => {
        try {
          const data = await apiGetFriend(friendUserId)
          if (!data) {
            return
          }
          // clear() 已切账号：旧请求的好友详情不能再 upsert 进新账号的 friends
          if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
            return
          }
          this.upsertFriend(convertFriend(data))
        } catch (error) {
          console.warn('[IM friendStore] fetchFriendInfo 失败', error)
        }
      })().finally(() => {
        if (pendingFetchFriendInfos.get(key) === promise) {
          pendingFetchFriendInfos.delete(key)
        }
      })
      pendingFetchFriendInfos.set(key, promise)
      return promise
    },

    // ==================== 申请-审批 ====================

    /** 发起好友申请：成功后等待对方同意（不直接落地为好友） */
    async applyFriendRequest(reqVO: ImFriendRequestApi.FriendRequestApplyReqVO): Promise<null | number> {
      return await apiApplyFriendRequest(reqVO)
    },

    /** 同意一条好友申请；后端会双向落库 + 推 FRIEND_ADD，本端等通知到达再 upsertFriend */
    async agreeFriendRequest(requestId: number) {
      await apiAgreeFriendRequest(requestId)
      await this.applyHandleResult(requestId, ImFriendRequestHandleResult.AGREED)
    },

    /** 拒绝一条好友申请 */
    async refuseFriendRequest(requestId: number, handleContent?: string) {
      await apiRefuseFriendRequest(requestId, handleContent)
      await this.applyHandleResult(requestId, ImFriendRequestHandleResult.REFUSED, handleContent)
    },

    /** 把 handleResult 应用到本地申请记录；找不到就按 id 单查兜底 upsert，避免破坏 id 倒序 */
    async applyHandleResult(
      requestId: number,
      result: number,
      handleContent?: string
    ): Promise<void> {
      const request = this.getFriendRequest(requestId)
      if (request) {
        request.handleResult = result
        if (handleContent !== undefined) {
          request.handleContent = handleContent
        }
        request.handleTime = Date.now()
        this.saveFriendRequest(request)
        return
      }
      await this.fetchFriendRequest(requestId)
    },

    /** 拉取「我相关」的好友申请列表首页（页面打开 / 收到 FRIEND_REQUEST_RECEIVED 时刷新）；pending 期间复用同一 Promise */
    async fetchFriendRequestList() {
      if (pendingFetchRequests) {
        const currentUserId = getCurrentUserId()
        if (
          pendingFetchRequests.epoch === storeEpoch &&
          pendingFetchRequests.userId === currentUserId
        ) {
          return pendingFetchRequests.promise
        }
      }
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      const promise = apiGetMyFriendRequestList(FRIEND_REQUEST_PAGE_SIZE)
        .then((list) => {
          if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
            return
          }
          const items = (list || []).map((request) => convertFriendRequest(request))
          this.friendRequests = items
          // 不足一页即没有更多；满页可能还有，等 loadMore 拉到 0 条再确定
          this.hasMoreFriendRequests = items.length >= FRIEND_REQUEST_PAGE_SIZE
          this.saveFriendRequestList()
        })
        .finally(() => {
          if (
            pendingFetchRequests?.epoch === requestEpoch &&
            pendingFetchRequests.userId === requestUserId
          ) {
            pendingFetchRequests = null
          }
        })
      pendingFetchRequests = { epoch: requestEpoch, userId: requestUserId, promise }
      return promise
    },

    /** 加载更多申请（按本地最旧 requestId 游标分页）；无更多 / pending 中直接返回 */
    async loadMoreFriendRequestList() {
      const requestUserId = getCurrentUserId()
      const hasSameFetchPending =
        pendingFetchRequests?.epoch === storeEpoch && pendingFetchRequests.userId === requestUserId
      if (!this.hasMoreFriendRequests || hasSameFetchPending) {
        return
      }
      if (
        pendingLoadMoreRequests?.epoch === storeEpoch &&
        pendingLoadMoreRequests.userId === requestUserId
      ) {
        return pendingLoadMoreRequests.promise
      }
      const oldest = this.friendRequests[this.friendRequests.length - 1]
      if (!oldest) {
        return this.fetchFriendRequestList()
      }
      const requestEpoch = storeEpoch
      const promise = apiGetMyFriendRequestList(FRIEND_REQUEST_PAGE_SIZE, oldest.id)
        .then((list) => {
          if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
            return
          }
          const items = (list || []).map((request) => convertFriendRequest(request))
          this.friendRequests.push(...items)
          this.hasMoreFriendRequests = items.length >= FRIEND_REQUEST_PAGE_SIZE
          this.saveFriendRequestList()
        })
        .finally(() => {
          if (
            pendingLoadMoreRequests?.epoch === requestEpoch &&
            pendingLoadMoreRequests.userId === requestUserId
          ) {
            pendingLoadMoreRequests = null
          }
        })
      pendingLoadMoreRequests = { epoch: requestEpoch, userId: requestUserId, promise }
      return promise
    },

    /** 按 id 查申请记录；列表是按 id 倒序的小列表，O(n) find 即可，不再维护 Map 索引 */
    getFriendRequest(requestId: number): FriendRequest | undefined {
      return this.friendRequests.find((request) => request.id === requestId)
    },

    /** 按 id 从后端单查并 upsert 到本地（dispatcher 兜底用，避免全量重拉）；后端带越权过滤 */
    async fetchFriendRequest(requestId: number) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      const data = await apiGetMyFriendRequest(requestId)
      if (!data) {
        return
      }
      // clear() 已切账号：旧请求的申请记录不能再写进新账号的 friendRequests
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      this.upsertFriendRequest(convertFriendRequest(data))
    },

    /** 合并单条好友申请：已有则按 id 覆盖；新记录按 id 倒序插入（比本地最旧还老则跳过，留给 loadMore 带回） */
    upsertFriendRequest(next: FriendRequest) {
      void this.upsertFriendRequestForPull(next).catch((error) =>
        console.warn('[IM friendStore] 本地好友申请写入失败', error)
      )
    },

    /** 合并单条好友申请 */
    async upsertFriendRequestForPull(next: FriendRequest): Promise<void> {
      const existing = this.getFriendRequest(next.id)
      if (existing) {
        Object.assign(existing, next)
        await this.saveFriendRequestRecord(existing)
        return
      }
      // 比本地最旧 id 还老：不入列表，让 loadMore 自然带回，避免破坏 id 倒序 / 后续 loadMore 重复 push
      const oldest = this.friendRequests[this.friendRequests.length - 1]
      if (oldest && next.id < oldest.id) {
        return
      }
      // 按 id 倒序找首个比自己小的位置插入；找不到则追加末尾
      const insertIndex = this.friendRequests.findIndex((request) => request.id < next.id)
      if (insertIndex === -1) {
        this.friendRequests.push(next)
      } else {
        this.friendRequests.splice(insertIndex, 0, next)
      }
      await this.saveFriendRequestRecord(next)
    },

    /** 增量拉取好友申请变更并合并（重连 / 离线补偿）；按 update_time + id 游标，已处理的按 handleResult 覆盖 */
    async pullFriendRequests() {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      const isActive = () => requestEpoch === storeEpoch && getCurrentUserId() === requestUserId
      await runIncrementalPull(
        StorageKeys.settings.friendRequestPullCursor,
        apiPullMyFriendRequestList,
        async (records) => {
          if (!isActive()) {
            return false
          }
          await Promise.all(
            records.map((vo) => this.upsertFriendRequestForPull(convertFriendRequest(vo)))
          )
          return true
        },
        isActive
      )
    },

    // ==================== 好友关系操作 ====================

    /** 删除好友（单向软删，本端置 DISABLE）；clear=true 时级联清理本地相关数据（如私聊会话），并透传后端给多端同步 */
    async deleteFriend(friendUserId: number, clear: boolean = true) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      await apiDeleteFriend(friendUserId, clear)
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      this.removeFriend(friendUserId, clear)
    },

    /** 切换免打扰：同步会话的 silent 字段，避免会话列表 silent 图标等 1210 推到才更新 */
    async setFriendSilent(friendUserId: number, silent: boolean) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      await apiUpdateFriend({ friendUserId, silent })
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      const friend = this.getFriend(friendUserId)
      if (friend) {
        friend.silent = silent
        const conversationStore = useConversationStore()
        conversationStore.updateConversation(ImConversationType.PRIVATE, friendUserId, { silent })
        this.saveFriend(friend)
      }
    },

    /** 切换联系人置顶 */
    async setFriendPinned(friendUserId: number, pinned: boolean) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      await apiUpdateFriend({ friendUserId, pinned })
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      const friend = this.getFriend(friendUserId)
      if (friend) {
        friend.pinned = pinned
        this.saveFriend(friend)
      }
    },

    /** 拉黑好友：本端乐观更新 + 调接口；后端 FRIEND_BLOCK 推到时由 dispatcher 兜底同步多端 */
    async blockFriend(friendUserId: number) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      await apiBlockFriend(friendUserId)
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      const friend = this.getFriend(friendUserId)
      if (friend) {
        friend.blocked = true
        this.saveFriend(friend)
      }
    },

    /** 移出黑名单：本端乐观更新 + 调接口；后端 FRIEND_UNBLOCK 推到时由 dispatcher 兜底同步多端 */
    async unblockFriend(friendUserId: number) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      await apiUnblockFriend(friendUserId)
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      const friend = this.getFriend(friendUserId)
      if (friend) {
        friend.blocked = false
        this.saveFriend(friend)
      }
    },

    /** 修改好友展示备注（仅自己可见） */
    async setFriendDisplayName(friendUserId: number, displayName: string) {
      const requestEpoch = storeEpoch
      const requestUserId = getCurrentUserId()
      const value = displayName.trim()
      // 后端 displayName 语义：null/undefined = 不改，"" = 清空，所以这里直接传 value（可能是空串）
      await apiUpdateFriend({ friendUserId, displayName: value })
      if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
        return
      }
      const friend = this.getFriend(friendUserId)
      if (friend) {
        friend.displayName = value
        const conversationStore = useConversationStore()
        conversationStore.updateConversation(ImConversationType.PRIVATE, friendUserId, {
          name: getFriendDisplayName(friend)
        })
        this.saveFriend(friend)
      }
    },

    /** 本地合并 / 新增某个好友（WebSocket 事件 & 手动刷新都用） */
    upsertFriend(friend: Friend) {
      void this.upsertFriendForPull(friend).catch((error) =>
        console.warn('[IM friendStore] 本地好友写入失败', error)
      )
    },

    /** 本地合并 / 新增某个好友 */
    async upsertFriendForPull(friend: Friend): Promise<void> {
      const index = this.friends.findIndex(
        (existing) => existing.friendUserId === friend.friendUserId
      )
      if (index === -1) {
        this.friends.push({
          ...friend,
          status: friend.status ?? CommonStatusEnum.ENABLE
        })
      } else {
        this.friends[index] = {
          ...this.friends[index],
          ...friend,
          status: friend.status ?? CommonStatusEnum.ENABLE
        }
      }
      const conversationStore = useConversationStore()
      const merged = this.getFriend(friend.friendUserId)
      conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, {
        name: merged ? getFriendDisplayName(merged) : friend.nickname,
        avatar: friend.avatar,
        silent: friend.silent
      })
      await this.saveFriendRecord(merged)
    },

    /** 本地标记删除（WebSocket FRIEND_DELETE 事件触发；clear=true 时级联清相关数据如私聊会话） */
    removeFriend(friendUserId: number, clear: boolean = true) {
      const friend = this.getFriend(friendUserId)
      if (friend) {
        // blocked 不动，跟后端 deleteFriend0「删好友期间保留拉黑状态」对齐
        friend.status = CommonStatusEnum.DISABLE
        friend.deleteTime = Date.now()
      }
      if (clear) {
        const conversationStore = useConversationStore()
        conversationStore.removePrivateConversation(friendUserId)
      }
      this.saveFriend(friend)
    },

    // ==================== WebSocket 事件 dispatcher（1201-1210 段） ====================

    /** FRIEND_REQUEST_RECEIVED(1203)：收到新申请；payload 已带申请方昵称 / 头像，按 requestId 直推 push 进列表 */
    applyFriendRequestReceivedNotification(payload: FriendNotificationPayload) {
      if (!payload.requestId) {
        return
      }
      const currentUserId = getCurrentUserId()
      const existingIndex = this.friendRequests.findIndex((item) => item.id === payload.requestId)
      if (existingIndex !== -1) {
        const existing = this.friendRequests.splice(existingIndex, 1)[0]
        if (!existing) {
          return
        }
        const next = {
          ...existing,
          fromUserId: payload.operatorUserId,
          toUserId: currentUserId,
          handleResult: ImFriendRequestHandleResult.UNHANDLED,
          applyContent: payload.applyContent,
          addSource: payload.addSource,
          createTime: Date.now(),
          fromNickname: payload.fromNickname,
          fromAvatar: payload.fromAvatar
        }
        this.friendRequests.unshift(next)
        this.saveFriendRequest(next)
        return
      }
      const next = {
        id: payload.requestId,
        fromUserId: payload.operatorUserId,
        toUserId: currentUserId,
        handleResult: ImFriendRequestHandleResult.UNHANDLED,
        applyContent: payload.applyContent,
        addSource: payload.addSource,
        createTime: Date.now(),
        fromNickname: payload.fromNickname,
        fromAvatar: payload.fromAvatar
      }
      this.friendRequests.unshift(next)
      this.saveFriendRequest(next)
    },

    /** FRIEND_REQUEST_APPROVED(1201)：我的申请被同意；按 requestId 更新状态（FRIEND_ADD 会另外推） */
    applyFriendRequestApprovedNotification(payload: FriendNotificationPayload) {
      if (!payload.requestId) {
        return
      }
      void this.applyHandleResult(payload.requestId, ImFriendRequestHandleResult.AGREED)
    },

    /** FRIEND_REQUEST_REJECTED(1202)：我的申请被拒绝；按 requestId 更新状态 */
    applyFriendRequestRejectedNotification(payload: FriendNotificationPayload) {
      if (!payload.requestId) {
        return
      }
      void this.applyHandleResult(
        payload.requestId,
        ImFriendRequestHandleResult.REFUSED,
        payload.handleContent
      )
    },

    /**
     * FRIEND_ADD(1204)：新增好友；本端拉取好友详情并入库
     * peerUserId 由 websocketStore 按帧 sender / receiver 算好传入：becomeFriends 单条入库后双方收到同一份 payload，
     * 本端真正的「对端」是帧上的另一个用户，不是 payload.friendUserId（payload 里固定是 toUserId）。
     */
    applyFriendAddNotification(_payload: FriendNotificationPayload, peerUserId: number) {
      if (this.isActiveFriend(peerUserId)) {
        return
      }
      void this.fetchFriendInfo(peerUserId)
    },

    /**
     * FRIEND_DELETE(1205)：好友被删除；本端清理 + 按 payload.clear 决定是否级联清会话（多端跟主操作端一致）
     * peerUserId 由 websocketStore 按帧 sender / receiver 算好传入；与 FRIEND_ADD 保持一致的 peer 推断
     */
    applyFriendDeleteNotification(payload: FriendNotificationPayload, peerUserId: number) {
      this.removeFriend(peerUserId, payload.clear !== false)
    },

    /** FRIEND_BLOCK(1207)：拉黑；多端同步 */
    applyFriendBlockNotification(payload: FriendNotificationPayload) {
      const friend = this.getFriend(payload.friendUserId)
      if (friend) {
        friend.blocked = true
        this.saveFriend(friend)
      }
    },

    /** FRIEND_UNBLOCK(1208)：移出黑名单；多端同步 */
    applyFriendUnblockNotification(payload: FriendNotificationPayload) {
      const friend = this.getFriend(payload.friendUserId)
      if (friend) {
        friend.blocked = false
        this.saveFriend(friend)
      }
    },

    /** FRIEND_INFO_UPDATED(1209)：好友资料变更（昵称 / 头像）；重拉详情 */
    applyFriendInfoUpdatedNotification(payload: FriendNotificationPayload) {
      void this.fetchFriendInfo(payload.friendUserId)
    },

    /** FRIEND_UPDATE(1210)：批量更新（备注 / 免打扰 / 联系人置顶）；多端同步 */
    applyFriendUpdateNotification(payload: FriendNotificationPayload) {
      const friend = this.getFriend(payload.friendUserId)
      if (!friend) {
        return
      }
      if (payload.displayName != null) {
        friend.displayName = payload.displayName
      }
      if (payload.silent != null) {
        friend.silent = payload.silent
      }
      if (payload.pinned != null) {
        friend.pinned = payload.pinned
      }
      const conversationStore = useConversationStore()
      conversationStore.updateConversation(ImConversationType.PRIVATE, payload.friendUserId, {
        name: getFriendDisplayName(friend),
        silent: friend.silent
      })
      this.saveFriend(friend)
    },

    /** 清空好友内存状态，并废弃未返回请求（pending Promise 置空 + storeEpoch++） */
    clear() {
      this.friends = []
      this.friendRequests = []
      this.loaded = false
      this.hasMoreFriendRequests = true
      pendingFetchFriends = null
      pendingFetchRequests = null
      pendingLoadMoreRequests = null
      pendingFetchFriendInfos.clear()
      storeEpoch++
    }
  }
})

function convertFriend(vo: ImFriendApi.FriendRespVO): Friend {
  return {
    id: vo.id,
    friendUserId: vo.friendUserId,
    nickname: vo.nickname || String(vo.friendUserId),
    nicknamePinyin: vo.nicknamePinyin,
    avatar: vo.avatar,
    silent: !!vo.silent,
    displayName: vo.displayName || '',
    displayNamePinyin: vo.displayNamePinyin,
    addSource: vo.addSource,
    pinned: !!vo.pinned,
    blocked: !!vo.blocked,
    status: vo.status,
    addTime: vo.addTime ? new Date(vo.addTime).getTime() : undefined,
    deleteTime: vo.deleteTime ? new Date(vo.deleteTime).getTime() : undefined
  }
}

function convertFriendRequest(vo: ImFriendRequestApi.FriendRequestRespVO): FriendRequest {
  return {
    id: vo.id,
    fromUserId: vo.fromUserId,
    toUserId: vo.toUserId,
    handleResult: vo.handleResult,
    applyContent: vo.applyContent,
    handleContent: vo.handleContent,
    addSource: vo.addSource,
    handleTime: vo.handleTime ? new Date(vo.handleTime).getTime() : undefined,
    createTime: vo.createTime ? new Date(vo.createTime).getTime() : 0,
    fromNickname: vo.fromNickname,
    fromAvatar: vo.fromAvatar,
    toNickname: vo.toNickname,
    toAvatar: vo.toAvatar
  }
}

export const useFriendStoreWithOut = () => useFriendStore()

// dev: 让 Pinia 的 actions / state 改动支持 HMR，避免每次改 store 都得硬刷
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useFriendStore, import.meta.hot))
}
