// Copyright © 2023 OpenIM SDK. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package conversation_msg

import (
	"context"
	"errors"
	_ "github.com/openimsdk/openim-sdk-core/v3/internal/common"
	"github.com/openimsdk/openim-sdk-core/v3/internal/util"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
	sdk "github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/server_api_params"
	"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
	"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
	"github.com/openimsdk/tools/utils/datautil"
	"sort"
	"strings"
	"time"

	"github.com/jinzhu/copier"

	"github.com/openimsdk/tools/log"

	"github.com/openimsdk/protocol/msg"
	"github.com/openimsdk/protocol/sdkws"

	pbConversation "github.com/openimsdk/protocol/conversation"
)

func (c *Conversation) setConversation(ctx context.Context, apiReq *pbConversation.SetConversationsReq, localConversation *model_struct.LocalConversation) error {
	apiReq.Conversation.ConversationID = localConversation.ConversationID
	apiReq.Conversation.ConversationType = localConversation.ConversationType
	apiReq.Conversation.UserID = localConversation.UserID
	apiReq.Conversation.GroupID = localConversation.GroupID
	apiReq.UserIDs = []string{c.loginUserID}
	if err := util.ApiPost(ctx, constant.SetConversationsRouter, apiReq, nil); err != nil {
		return err
	}
	return nil
}

func (c *Conversation) getServerConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error) {
	resp, err := util.CallApi[pbConversation.GetAllConversationsResp](ctx, constant.GetAllConversationsRouter, pbConversation.GetAllConversationsReq{OwnerUserID: c.loginUserID})
	if err != nil {
		return nil, err
	}
	return datautil.Batch(ServerConversationToLocal, resp.Conversations), nil
}

func (c *Conversation) getServerConversationsByIDs(ctx context.Context, conversations []string) ([]*model_struct.LocalConversation, error) {
	resp, err := util.CallApi[pbConversation.GetConversationsResp](ctx, constant.GetConversationsRouter, pbConversation.GetConversationsReq{OwnerUserID: c.loginUserID, ConversationIDs: conversations})
	if err != nil {
		return nil, err
	}
	return datautil.Batch(ServerConversationToLocal, resp.Conversations), nil
}

func (c *Conversation) getServerHasReadAndMaxSeqs(ctx context.Context, conversationIDs ...string) (map[string]*msg.Seqs, error) {
	resp := &msg.GetConversationsHasReadAndMaxSeqResp{}
	req := msg.GetConversationsHasReadAndMaxSeqReq{UserID: c.loginUserID}
	req.ConversationIDs = conversationIDs
	err := util.ApiPost(ctx, constant.GetConversationsHasReadAndMaxSeqRouter, &req, resp)
	if err != nil {
		log.ZError(ctx, "getServerHasReadAndMaxSeqs err", err)
		return nil, err
	}
	return resp.Seqs, nil
}

func (c *Conversation) getAdvancedHistoryMessageList(ctx context.Context, req sdk.GetAdvancedHistoryMessageListParams, isReverse bool) (*sdk.GetAdvancedHistoryMessageListCallback, error) {
	t := time.Now()
	var messageListCallback sdk.GetAdvancedHistoryMessageListCallback
	var conversationID string
	var startTime int64
	var sessionType int
	var list []*model_struct.LocalChatLog
	var err error
	var messageList sdk_struct.NewMsgList
	var notStartTime bool
	conversationID = req.ConversationID
	lc, err := c.db.GetConversation(ctx, conversationID)
	if err != nil {
		return nil, err
	}
	sessionType = int(lc.ConversationType)
	if req.StartClientMsgID == "" {
		notStartTime = true
	} else {
		m, err := c.db.GetMessage(ctx, conversationID, req.StartClientMsgID)
		if err != nil {
			return nil, err
		}
		startTime = m.SendTime
	}
	log.ZDebug(ctx, "Assembly conversation parameters", "cost time", time.Since(t), "conversationID",
		conversationID, "startTime:", startTime, "count:", req.Count, "not start_time", notStartTime)
	t = time.Now()
	if notStartTime {
		list, err = c.db.GetMessageListNoTime(ctx, conversationID, req.Count, isReverse)
	} else {
		list, err = c.db.GetMessageList(ctx, conversationID, req.Count, startTime, isReverse)
	}
	log.ZDebug(ctx, "db get messageList", "cost time", time.Since(t), "len", len(list), "err",
		err, "conversationID", conversationID)

	if err != nil {
		return nil, err
	}
	rawMessageLength := len(list)
	t = time.Now()
	if rawMessageLength < req.Count {
		maxSeq, minSeq, lostSeqListLength := c.messageBlocksInternalContinuityCheck(ctx,
			conversationID, notStartTime, isReverse, req.Count, startTime, &list, &messageListCallback)
		_ = c.messageBlocksBetweenContinuityCheck(ctx, req.LastMinSeq, maxSeq, conversationID,
			notStartTime, isReverse, req.Count, startTime, &list, &messageListCallback)
		if minSeq == 1 && lostSeqListLength == 0 {
			messageListCallback.IsEnd = true
		} else {
			c.messageBlocksEndContinuityCheck(ctx, minSeq, conversationID, notStartTime, isReverse,
				req.Count, startTime, &list, &messageListCallback)
		}
	} else {
		maxSeq, _, _ := c.messageBlocksInternalContinuityCheck(ctx, conversationID, notStartTime, isReverse,
			req.Count, startTime, &list, &messageListCallback)
		c.messageBlocksBetweenContinuityCheck(ctx, req.LastMinSeq, maxSeq, conversationID, notStartTime,
			isReverse, req.Count, startTime, &list, &messageListCallback)

	}
	log.ZDebug(ctx, "pull message", "pull cost time", time.Since(t))
	t = time.Now()
	var thisMinSeq int64
	for _, v := range list {
		if v.Seq != 0 && thisMinSeq == 0 {
			thisMinSeq = v.Seq
		}
		if v.Seq < thisMinSeq && v.Seq != 0 {
			thisMinSeq = v.Seq
		}
		if v.Status >= constant.MsgStatusHasDeleted {
			log.ZDebug(ctx, "this message has been deleted or exception message", "msg", v)
			continue
		}
		temp := sdk_struct.MsgStruct{}
		temp.ClientMsgID = v.ClientMsgID
		temp.ServerMsgID = v.ServerMsgID
		temp.CreateTime = v.CreateTime
		temp.SendTime = v.SendTime
		temp.SessionType = v.SessionType
		temp.SendID = v.SendID
		temp.RecvID = v.RecvID
		temp.MsgFrom = v.MsgFrom
		temp.ContentType = v.ContentType
		temp.SenderPlatformID = v.SenderPlatformID
		temp.SenderNickname = v.SenderNickname
		temp.SenderFaceURL = v.SenderFaceURL
		temp.Content = v.Content
		temp.Seq = v.Seq
		temp.IsRead = v.IsRead
		temp.Status = v.Status
		var attachedInfo sdk_struct.AttachedInfoElem
		_ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
		temp.AttachedInfoElem = &attachedInfo
		temp.Ex = v.Ex
		temp.LocalEx = v.LocalEx
		err := c.msgHandleByContentType(&temp)
		if err != nil {
			log.ZError(ctx, "Parsing data error", err, "temp", temp)
			continue
		}
		switch sessionType {
		case constant.GroupChatType:
			fallthrough
		case constant.SuperGroupChatType:
			temp.GroupID = temp.RecvID
			temp.RecvID = c.loginUserID
		}
		if attachedInfo.IsPrivateChat && temp.SendTime+int64(attachedInfo.BurnDuration) < time.Now().Unix() {
			continue
		}
		messageList = append(messageList, &temp)
	}
	log.ZDebug(ctx, "message convert and unmarshal", "unmarshal cost time", time.Since(t))
	t = time.Now()
	if !isReverse {
		sort.Sort(messageList)
	}
	log.ZDebug(ctx, "sort", "sort cost time", time.Since(t))
	messageListCallback.MessageList = messageList
	if thisMinSeq == 0 {
		thisMinSeq = req.LastMinSeq
	}
	messageListCallback.LastMinSeq = thisMinSeq
	return &messageListCallback, nil

}

func (c *Conversation) typingStatusUpdate(ctx context.Context, recvID, msgTip string) error {
	if recvID == "" {
		return sdkerrs.ErrArgs
	}
	s := sdk_struct.MsgStruct{}
	err := c.initBasicInfo(ctx, &s, constant.UserMsgType, constant.Typing)
	if err != nil {
		return err
	}
	s.RecvID = recvID
	s.SessionType = constant.SingleChatType
	typingElem := sdk_struct.TypingElem{}
	typingElem.MsgTips = msgTip
	s.Content = utils.StructToJsonString(typingElem)
	options := make(map[string]bool, 6)
	utils.SetSwitchFromOptions(options, constant.IsHistory, false)
	utils.SetSwitchFromOptions(options, constant.IsPersistent, false)
	utils.SetSwitchFromOptions(options, constant.IsSenderSync, false)
	utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false)
	utils.SetSwitchFromOptions(options, constant.IsSenderConversationUpdate, false)
	utils.SetSwitchFromOptions(options, constant.IsUnreadCount, false)
	utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)

	var wsMsgData sdkws.MsgData
	copier.Copy(&wsMsgData, s)
	wsMsgData.Content = []byte(s.Content)
	wsMsgData.CreateTime = s.CreateTime
	wsMsgData.Options = options
	var sendMsgResp sdkws.UserSendMsgResp
	err = c.LongConnMgr.SendReqWaitResp(ctx, &wsMsgData, constant.SendMsg, &sendMsgResp)
	if err != nil {
		log.ZError(ctx, "send msg to server failed", err, "message", s)
		return err
	}
	return nil

}

//	funcation (c *Conversation) markMessageAsReadByConID(callback open_im_sdk_callback.Base, msgIDList sdk.MarkMessageAsReadByConIDParams, conversationID, operationID string) {
//		var localMessage db.LocalChatLog
//		var newMessageIDList []string
//		messages, err := c.db.GetMultipleMessage(msgIDList)
//		common.CheckDBErrCallback(callback, err, operationID)
//		for _, v := range messages {
//			if v.IsRead == false && v.ContentType < constant.NotificationBegin && v.SendID != c.loginUserID {
//				newMessageIDList = append(newMessageIDList, v.ClientMsgID)
//			}
//		}
//		if len(newMessageIDList) == 0 {
//			common.CheckAnyErrCallback(callback, 201, errors.New("message has been marked read or sender is yourself"), operationID)
//		}
//		conversationID := c.getConversationIDBySessionType(userID, constant.SingleChatType)
//		s := sdk_struct.MsgStruct{}
//		c.initBasicInfo(&s, constant.UserMsgType, constant.HasReadReceipt, operationID)
//		s.Content = utils.StructToJsonString(newMessageIDList)
//		options := make(map[string]bool, 5)
//		utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, false)
//		utils.SetSwitchFromOptions(options, constant.IsSenderConversationUpdate, false)
//		utils.SetSwitchFromOptions(options, constant.IsUnreadCount, false)
//		utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
//		//If there is an error, the coroutine ends, so judgment is not  required
//		resp, _ := c.InternalSendMessage(callback, &s, userID, "", operationID, &server_api_params.OfflinePushInfo{}, false, options)
//		s.ServerMsgID = resp.ServerMsgID
//		s.SendTime = resp.SendTime
//		s.Status = constant.MsgStatusFiltered
//		msgStructToLocalChatLog(&localMessage, &s)
//		err = c.db.InsertMessage(&localMessage)
//		if err != nil {
//			log.Error(operationID, "inset into chat log err", localMessage, s, err.Error())
//		}
//		err2 := c.db.UpdateMessageHasRead(userID, newMessageIDList, constant.SingleChatType)
//		if err2 != nil {
//			log.Error(operationID, "update message has read error", newMessageIDList, userID, err2.Error())
//		}
//		_ = common.TriggerCmdUpdateConversation(common.UpdateConNode{ConID: conversationID, Action: constant.UpdateLatestMessageChange}, c.ch)
//		//_ = common.TriggerCmdUpdateConversation(common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}, c.ch)
//	}

func (c *Conversation) insertMessageToLocalStorage(ctx context.Context, conversationID string, s *model_struct.LocalChatLog) error {
	return c.db.InsertMessage(ctx, conversationID, s)
}

func (c *Conversation) judgeMultipleSubString(keywordList []string, main string, keywordListMatchType int) bool {
	if len(keywordList) == 0 {
		return true
	}
	if keywordListMatchType == constant.KeywordMatchOr {
		for _, v := range keywordList {
			if utils.KMP(main, v) {
				return true
			}
		}
		return false
	} else {
		for _, v := range keywordList {
			if !utils.KMP(main, v) {
				return false
			}
		}
	}
	return true
}

// searchLocalMessages searches for local messages based on the given search parameters.
func (c *Conversation) searchLocalMessages(ctx context.Context, searchParam *sdk.SearchLocalMessagesParams) (*sdk.SearchLocalMessagesCallback, error) {
	var r sdk.SearchLocalMessagesCallback                                   // Initialize the result structure
	var startTime, endTime int64                                            // Variables to hold start and end times for the search
	var list []*model_struct.LocalChatLog                                   // Slice to store the search results
	conversationMap := make(map[string]*sdk.SearchByConversationResult, 10) // Map to store results grouped by conversation, with initial capacity of 10
	var err error                                                           // Variable to store any errors encountered
	var conversationID string                                               // Variable to store the current conversation ID

	// Set the end time for the search; if SearchTimePosition is 0, use the current timestamp
	if searchParam.SearchTimePosition == 0 {
		endTime = utils.GetCurrentTimestampBySecond()
	} else {
		endTime = searchParam.SearchTimePosition
	}

	// Set the start time based on the specified time period
	if searchParam.SearchTimePeriod != 0 {
		startTime = endTime - searchParam.SearchTimePeriod
	}
	// Convert start and end times to milliseconds
	startTime = utils.UnixSecondToTime(startTime).UnixNano() / 1e6
	endTime = utils.UnixSecondToTime(endTime).UnixNano() / 1e6

	// Validate that either keyword list or message type list is provided
	if len(searchParam.KeywordList) == 0 && len(searchParam.MessageTypeList) == 0 {
		return nil, errors.New("keywordlist and messageTypelist all null")
	}

	// Search in a specific conversation if ConversationID is provided
	if searchParam.ConversationID != "" {
		// Validate pagination parameters
		if searchParam.PageIndex < 1 || searchParam.Count < 1 {
			return nil, errors.New("page or count is null")
		}
		offset := (searchParam.PageIndex - 1) * searchParam.Count
		_, err := c.db.GetConversation(ctx, searchParam.ConversationID)
		if err != nil {
			return nil, err
		}
		// Search by content type or keyword based on provided parameters
		if len(searchParam.MessageTypeList) != 0 && len(searchParam.KeywordList) == 0 {
			list, err = c.db.SearchMessageByContentType(ctx, searchParam.MessageTypeList, searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
		} else {
			newContentTypeList := func(list []int) (result []int) {
				for _, v := range list {
					if utils.IsContainInt(v, SearchContentType) {
						result = append(result, v)
					}
				}
				return result
			}(searchParam.MessageTypeList)
			if len(newContentTypeList) == 0 {
				newContentTypeList = SearchContentType
			}
			list, err = c.db.SearchMessageByKeyword(ctx, newContentTypeList, searchParam.KeywordList, searchParam.KeywordListMatchType,
				searchParam.ConversationID, startTime, endTime, offset, searchParam.Count)
		}
	} else {
		// Comprehensive search across all conversations
		if len(searchParam.MessageTypeList) == 0 {
			searchParam.MessageTypeList = SearchContentType
		}
		list, err = c.messageController.SearchMessageByContentTypeAndKeyword(ctx, searchParam.MessageTypeList, searchParam.KeywordList, searchParam.KeywordListMatchType, startTime, endTime)
	}

	// Handle any errors encountered during the search
	if err != nil {
		return nil, err
	}

	// Logging and processing each message in the search results
	//localChatLogToMsgStruct(&messageList, list)

	//log.Debug("hahh",utils.KMP("SSSsdf3434","s"))
	//log.Debug("hahh",utils.KMP("SSSsdf3434","g"))
	//log.Debug("hahh",utils.KMP("SSSsdf3434","3434"))
	//log.Debug("hahh",utils.KMP("SSSsdf3434","F3434"))
	//log.Debug("hahh",utils.KMP("SSSsdf3434","SDF3"))
	// log.Debug("", "get raw data length is", len(list))
	log.ZDebug(ctx, "get raw data length is", len(list))

	for _, v := range list {
		temp := sdk_struct.MsgStruct{}
		temp.ClientMsgID = v.ClientMsgID
		temp.ServerMsgID = v.ServerMsgID
		temp.CreateTime = v.CreateTime
		temp.SendTime = v.SendTime
		temp.SessionType = v.SessionType
		temp.SendID = v.SendID
		temp.RecvID = v.RecvID
		temp.MsgFrom = v.MsgFrom
		temp.ContentType = v.ContentType
		temp.SenderPlatformID = v.SenderPlatformID
		temp.SenderNickname = v.SenderNickname
		temp.SenderFaceURL = v.SenderFaceURL
		temp.Content = v.Content
		temp.Seq = v.Seq
		temp.IsRead = v.IsRead
		temp.Status = v.Status
		var attachedInfo sdk_struct.AttachedInfoElem
		_ = utils.JsonStringToStruct(v.AttachedInfo, &attachedInfo)
		temp.AttachedInfoElem = &attachedInfo
		temp.Ex = v.Ex
		temp.LocalEx = v.LocalEx
		err := c.msgHandleByContentType(&temp)
		if err != nil {
			// log.Error("", "Parsing data error:", err.Error(), temp)
			log.ZError(ctx, "Parsing data error:", err, "msg", temp)
			continue
		}
		if c.filterMsg(&temp, searchParam) {
			continue
		}
		// Determine the conversation ID based on the session type
		switch temp.SessionType {
		case constant.SingleChatType:
			if temp.SendID == c.loginUserID {
				conversationID = c.getConversationIDBySessionType(temp.RecvID, constant.SingleChatType)
			} else {
				conversationID = c.getConversationIDBySessionType(temp.SendID, constant.SingleChatType)
			}
		case constant.GroupChatType:
			temp.GroupID = temp.RecvID
			temp.RecvID = c.loginUserID
			conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.GroupChatType)
		case constant.SuperGroupChatType:
			temp.GroupID = temp.RecvID
			temp.RecvID = c.loginUserID
			conversationID = c.getConversationIDBySessionType(temp.GroupID, constant.SuperGroupChatType)
		}
		// Populate the conversationMap with search results
		if oldItem, ok := conversationMap[conversationID]; !ok {
			searchResultItem := sdk.SearchByConversationResult{}
			localConversation, err := c.db.GetConversation(ctx, conversationID)
			if err != nil {
				// log.Error("", "get conversation err ", err.Error(), conversationID)
				continue
			}
			searchResultItem.ConversationID = conversationID
			searchResultItem.FaceURL = localConversation.FaceURL
			searchResultItem.ShowName = localConversation.ShowName
			searchResultItem.LatestMsgSendTime = localConversation.LatestMsgSendTime
			searchResultItem.ConversationType = localConversation.ConversationType
			searchResultItem.MessageList = append(searchResultItem.MessageList, &temp)
			searchResultItem.MessageCount++
			conversationMap[conversationID] = &searchResultItem
		} else {
			oldItem.MessageCount++
			oldItem.MessageList = append(oldItem.MessageList, &temp)
			conversationMap[conversationID] = oldItem
		}
	}

	// Compile the results from the conversationMap into the response structure
	for _, v := range conversationMap {
		r.SearchResultItems = append(r.SearchResultItems, v)
		r.TotalCount += v.MessageCount
	}

	// Sort the search results based on the latest message send time
	sort.Slice(r.SearchResultItems, func(i, j int) bool {
		return r.SearchResultItems[i].LatestMsgSendTime > r.SearchResultItems[j].LatestMsgSendTime
	})

	return &r, nil // Return the final search results
}

// true is filter, false is not filter
func (c *Conversation) filterMsg(temp *sdk_struct.MsgStruct, searchParam *sdk.SearchLocalMessagesParams) bool {
	switch temp.ContentType {
	case constant.Text:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.TextElem.Content,
			searchParam.KeywordListMatchType)
	case constant.AtText:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.AtTextElem.Text,
			searchParam.KeywordListMatchType)
	case constant.File:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.FileElem.FileName,
			searchParam.KeywordListMatchType)
	case constant.Merger:
		if !c.judgeMultipleSubString(searchParam.KeywordList, temp.MergeElem.Title, searchParam.KeywordListMatchType) {
			for _, msgStruct := range temp.MergeElem.MultiMessage {
				if c.filterMsg(msgStruct, searchParam) {
					continue
				} else {
					break
				}
			}
		}
	case constant.Card:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.CardElem.Nickname,
			searchParam.KeywordListMatchType)
	case constant.Location:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.LocationElem.Description,
			searchParam.KeywordListMatchType)
	case constant.Custom:
		return !c.judgeMultipleSubString(searchParam.KeywordList, temp.CustomElem.Description,
			searchParam.KeywordListMatchType)
	case constant.Quote:
		if !c.judgeMultipleSubString(searchParam.KeywordList, temp.QuoteElem.Text, searchParam.KeywordListMatchType) {
			return c.filterMsg(temp.QuoteElem.QuoteMessage, searchParam)
		}
	case constant.Picture:
		fallthrough
	case constant.Video:
		if len(searchParam.KeywordList) == 0 {
			return false
		} else {
			return true
		}
	default:
		return true
	}
	return false
}

func (c *Conversation) delMsgBySeq(seqList []uint32) error {
	var SPLIT = 1000
	for i := 0; i < len(seqList)/SPLIT; i++ {
		if err := c.delMsgBySeqSplit(seqList[i*SPLIT : (i+1)*SPLIT]); err != nil {
			return utils.Wrap(err, "")
		}
	}
	return nil
}

func (c *Conversation) delMsgBySeqSplit(seqList []uint32) error {
	// var req server_api_params.DelMsgListReq
	// req.SeqList = seqList
	// req.OperationID = utils.OperationIDGenerator()
	// req.OpUserID = c.loginUserID
	// req.UserID = c.loginUserID
	// operationID := req.OperationID

	// err := c.SendReqWaitResp(context.Background(), &req, constant.WsDelMsg, 30, c.loginUserID)
	// if err != nil {
	// 	return utils.Wrap(err, "SendReqWaitResp failed")
	// }
	// var delResp server_api_params.DelMsgListResp
	// err = proto.Unmarshal(resp.Data, &delResp)
	// if err != nil {
	// 	log.Error(operationID, "Unmarshal failed ", err.Error())
	// 	return utils.Wrap(err, "Unmarshal failed")
	// }
	return nil
}

// old WS method
//funcation (c *Conversation) deleteMessageFromSvr(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, operationID string) {
//	seq, err := c.db.GetMsgSeqByClientMsgID(s.ClientMsgID)
//	common.CheckDBErrCallback(callback, err, operationID)
//	if seq == 0 {
//		err = errors.New("seq == 0 ")
//		common.CheckArgsErrCallback(callback, err, operationID)
//	}
//	seqList := []uint32{seq}
//	err = c.delMsgBySeq(seqList)
//	common.CheckArgsErrCallback(callback, err, operationID)
//}

func isContainMessageReaction(reactionType int, list []*sdk_struct.ReactionElem) (bool, *sdk_struct.ReactionElem) {
	for _, v := range list {
		if v.Type == reactionType {
			return true, v
		}
	}
	return false, nil
}

func isContainUserReactionElem(useID string, list []*sdk_struct.UserReactionElem) (bool, *sdk_struct.UserReactionElem) {
	for _, v := range list {
		if v.UserID == useID {
			return true, v
		}
	}
	return false, nil
}

func DeleteUserReactionElem(a []*sdk_struct.UserReactionElem, userID string) []*sdk_struct.UserReactionElem {
	j := 0
	for _, v := range a {
		if v.UserID != userID {
			a[j] = v
			j++
		}
	}
	return a[:j]
}

func (c *Conversation) setMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.SetMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
	return nil, nil
	//message, err := c.db.GetMessageController(ctx, s)
	//if err != nil {
	//	return nil, err
	//}
	//if message.Status != constant.MsgStatusSendSuccess {
	//	return nil, errors.New("only send success message can modify reaction extensions")
	//}
	//if message.SessionType != constant.SuperGroupChatType {
	//	return nil, errors.New("currently only support super group message")
	//
	//}
	//extendMsg, _ := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
	//temp := make(map[string]*server_api_params.KeyValue)
	//_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
	//reqTemp := make(map[string]*server_api_params.KeyValue)
	//for _, v := range req {
	//	if value, ok := temp[v.TypeKey]; ok {
	//		v.LatestUpdateTime = value.LatestUpdateTime
	//	}
	//	reqTemp[v.TypeKey] = v
	//}
	//var sourceID string
	//switch message.SessionType {
	//case constant.SingleChatType:
	//	if message.SendID == c.loginUserID {
	//		sourceID = message.RecvID
	//	} else {
	//		sourceID = message.SendID
	//	}
	//case constant.NotificationChatType:
	//	sourceID = message.RecvID
	//case constant.GroupChatType, constant.SuperGroupChatType:
	//	sourceID = message.RecvID
	//}
	//var apiReq server_api_params.SetMessageReactionExtensionsReq
	//apiReq.IsReact = message.IsReact
	//apiReq.ClientMsgID = message.ClientMsgID
	//apiReq.SourceID = sourceID
	//apiReq.SessionType = message.SessionType
	//apiReq.IsExternalExtensions = message.IsExternalExtensions
	//apiReq.ReactionExtensionList = reqTemp
	//apiReq.OperationID = ""
	//apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
	//resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.SetMessageReactionExtensionsRouter, &apiReq)
	//if err != nil {
	//	return nil, err
	//}
	//var msg model_struct.LocalChatLogReactionExtensions
	//msg.ClientMsgID = message.ClientMsgID
	//resultKeyMap := make(map[string]*sdkws.KeyValue)
	//for _, v := range resp.Result {
	//	if v.ErrCode == 0 {
	//		temp := new(sdkws.KeyValue)
	//		temp.TypeKey = v.TypeKey
	//		temp.Value = v.Value
	//		temp.LatestUpdateTime = v.LatestUpdateTime
	//		resultKeyMap[v.TypeKey] = temp
	//	}
	//}
	//err = c.db.GetAndUpdateMessageReactionExtension(ctx, message.ClientMsgID, resultKeyMap)
	//if err != nil {
	//	log.Error("", "GetAndUpdateMessageReactionExtension err:", err.Error())
	//}
	//if !message.IsReact {
	//	message.IsReact = resp.IsReact
	//	message.MsgFirstModifyTime = resp.MsgFirstModifyTime
	//	err = c.db.UpdateMessageController(ctx, message)
	//	if err != nil {
	//		log.Error("", "UpdateMessageController err:", err.Error(), message)
	//
	//	}
	//}
	//return resp.Result, nil
}

func (c *Conversation) addMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.AddMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
	return nil, nil
	//message, err := c.db.GetMessageController(ctx, s)
	//if err != nil {
	//	return nil, err
	//}
	//if message.Status != constant.MsgStatusSendSuccess || message.Seq == 0 {
	//	return nil, errors.New("only send success message can modify reaction extensions")
	//}
	//reqTemp := make(map[string]*server_api_params.KeyValue)
	//extendMsg, err := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
	//if err == nil && extendMsg != nil {
	//	temp := make(map[string]*server_api_params.KeyValue)
	//	_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
	//	for _, v := range req {
	//		if value, ok := temp[v.TypeKey]; ok {
	//			v.LatestUpdateTime = value.LatestUpdateTime
	//		}
	//		reqTemp[v.TypeKey] = v
	//	}
	//} else {
	//	for _, v := range req {
	//		reqTemp[v.TypeKey] = v
	//	}
	//}
	//var sourceID string
	//switch message.SessionType {
	//case constant.SingleChatType:
	//	if message.SendID == c.loginUserID {
	//		sourceID = message.RecvID
	//	} else {
	//		sourceID = message.SendID
	//	}
	//case constant.NotificationChatType:
	//	sourceID = message.RecvID
	//case constant.GroupChatType, constant.SuperGroupChatType:
	//	sourceID = message.RecvID
	//}
	//var apiReq server_api_params.AddMessageReactionExtensionsReq
	//apiReq.IsReact = message.IsReact
	//apiReq.ClientMsgID = message.ClientMsgID
	//apiReq.SourceID = sourceID
	//apiReq.SessionType = message.SessionType
	//apiReq.IsExternalExtensions = message.IsExternalExtensions
	//apiReq.ReactionExtensionList = reqTemp
	//apiReq.OperationID = ""
	//apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
	//apiReq.Seq = message.Seq
	//
	//resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.AddMessageReactionExtensionsRouter, &apiReq)
	//if err != nil {
	//	return nil, err
	//}
	//log.Debug("", "api return:", message.IsReact, resp)
	//if !message.IsReact {
	//	message.IsReact = resp.IsReact
	//	message.MsgFirstModifyTime = resp.MsgFirstModifyTime
	//	err = c.db.UpdateMessageController(ctx, message)
	//	if err != nil {
	//		log.Error("", "UpdateMessageController err:", err.Error(), message)
	//	}
	//}
	//return resp.Result, nil
}

func (c *Conversation) deleteMessageReactionExtensions(ctx context.Context, s *sdk_struct.MsgStruct, req sdk.DeleteMessageReactionExtensionsParams) ([]*server_api_params.ExtensionResult, error) {
	// message, err := c.GetMessageController(ctx, s)
	// if err != nil {
	// 	return nil, err
	// }
	// if message.Status != constant.MsgStatusSendSuccess {
	// 	return nil, errors.New("only send success message can modify reaction extensions")
	// }
	// if message.SessionType != constant.SuperGroupChatType {
	// 	return nil, errors.New("currently only support super group message")

	// }
	// extendMsg, _ := c.db.GetMessageReactionExtension(ctx, message.ClientMsgID)
	// temp := make(map[string]*server_api_params.KeyValue)
	// _ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
	// var reqTemp []*server_api_params.KeyValue
	// for _, v := range req {
	// 	if value, ok := temp[v]; ok {
	// 		var tt server_api_params.KeyValue
	// 		tt.LatestUpdateTime = value.LatestUpdateTime
	// 		tt.TypeKey = v
	// 		reqTemp = append(reqTemp, &tt)
	// 	}
	// }
	// var sourceID string
	// switch message.SessionType {
	// case constant.SingleChatType:
	// 	if message.SendID == c.loginUserID {
	// 		sourceID = message.RecvID
	// 	} else {
	// 		sourceID = message.SendID
	// 	}
	// case constant.NotificationChatType:
	// 	sourceID = message.RecvID
	// case constant.GroupChatType, constant.SuperGroupChatType:
	// 	sourceID = message.RecvID
	// }
	// var apiReq server_api_params.DeleteMessageReactionExtensionsReq
	// apiReq.ClientMsgID = message.ClientMsgID
	// apiReq.SourceID = sourceID
	// apiReq.SessionType = message.SessionType
	// apiReq.ReactionExtensionList = reqTemp
	// apiReq.OperationID = ""
	// apiReq.IsExternalExtensions = message.IsExternalExtensions
	// apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
	// resp, err := util.CallApi[server_api_params.ApiResult](ctx, constant.AddMessageReactionExtensionsRouter, &apiReq)
	// if err != nil {
	// 	return nil, err
	// }
	// var msg model_struct.LocalChatLogReactionExtensions
	// msg.ClientMsgID = message.ClientMsgID
	// resultKeyMap := make(map[string]*sdkws.KeyValue)
	// for _, v := range resp.Result {
	// 	if v.ErrCode == 0 {
	// 		temp := new(sdkws.KeyValue)
	// 		temp.TypeKey = v.TypeKey
	// 		resultKeyMap[v.TypeKey] = temp
	// 	}
	// }
	// err = c.db.DeleteAndUpdateMessageReactionExtension(ctx, message.ClientMsgID, resultKeyMap)
	// if err != nil {
	// 	log.Error("", "GetAndUpdateMessageReactionExtension err:", err.Error())
	// }
	// return resp.Result, nil
	return nil, nil
}

type syncReactionExtensionParams struct {
	MessageList         []*model_struct.LocalChatLog
	SessionType         int32
	SourceID            string
	IsExternalExtension bool
	ExtendMessageList   []*model_struct.LocalChatLogReactionExtensions
	TypeKeyList         []string
}

func (c *Conversation) getMessageListReactionExtensions(ctx context.Context, conversationID string, messageList []*sdk_struct.MsgStruct) ([]*server_api_params.SingleMessageExtensionResult, error) {
	if len(messageList) == 0 {
		return nil, errors.New("message list is null")
	}
	var msgIDs []string
	var sourceID string
	var sessionType int32
	var isExternalExtension bool
	for _, msgStruct := range messageList {
		switch msgStruct.SessionType {
		case constant.SingleChatType:
			if msgStruct.SendID == c.loginUserID {
				sourceID = msgStruct.RecvID
			} else {
				sourceID = msgStruct.SendID
			}
		case constant.NotificationChatType:
			sourceID = msgStruct.RecvID
		case constant.GroupChatType, constant.SuperGroupChatType:
			sourceID = msgStruct.GroupID
		}
		sessionType = msgStruct.SessionType
		msgIDs = append(msgIDs, msgStruct.ClientMsgID)
	}
	isExternalExtension = c.IsExternalExtensions
	localMessageList, err := c.db.GetMessagesByClientMsgIDs(ctx, conversationID, msgIDs)
	if err != nil {
		return nil, err
	}
	for _, v := range localMessageList {
		if v.IsReact != true {
			return nil, errors.New("have not reaction message in message list:" + v.ClientMsgID)
		}
	}
	var result server_api_params.GetMessageListReactionExtensionsResp
	extendMessage, _ := c.db.GetMultipleMessageReactionExtension(ctx, msgIDs)
	for _, v := range extendMessage {
		var singleResult server_api_params.SingleMessageExtensionResult
		// temp := make(map[string]*sdkws.KeyValue)
		// _ = json.Unmarshal(v.LocalReactionExtensions, &temp)
		singleResult.ClientMsgID = v.ClientMsgID
		// singleResult.ReactionExtensionList = temp
		result = append(result, &singleResult)
	}
	args := syncReactionExtensionParams{}
	args.MessageList = localMessageList
	args.SourceID = sourceID
	args.SessionType = sessionType
	args.ExtendMessageList = extendMessage
	args.IsExternalExtension = isExternalExtension
	_ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
		OperationID: "",
		Action:      constant.SyncMessageListReactionExtensions,
		Args:        args,
	}, c.GetCh())
	return result, nil

}

//	funcation (c *Conversation) getMessageListSomeReactionExtensions(callback open_im_sdk_callback.Base, messageList []*sdk_struct.MsgStruct, keyList []string, operationID string) server_api_params.GetMessageListReactionExtensionsResp {
//		if len(messageList) == 0 {
//			common.CheckAnyErrCallback(callback, 201, errors.New("message list is null"), operationID)
//		}
//		var msgIDList []string
//		var sourceID string
//		var sessionType int32
//		var isExternalExtension bool
//		for _, msgStruct := range messageList {
//			switch msgStruct.SessionType {
//			case constant.SingleChatType:
//				if msgStruct.SendID == c.loginUserID {
//					sourceID = msgStruct.RecvID
//				} else {
//					sourceID = msgStruct.SendID
//				}
//			case constant.NotificationChatType:
//				sourceID = msgStruct.RecvID
//			case constant.GroupChatType, constant.SuperGroupChatType:
//				sourceID = msgStruct.GroupID
//			}
//			sessionType = msgStruct.SessionType
//			isExternalExtension = msgStruct.IsExternalExtensions
//			msgIDList = append(msgIDList, msgStruct.ClientMsgID)
//		}
//		localMessageList, err := c.db.GetMultipleMessageController(msgIDList, sourceID, sessionType)
//		common.CheckDBErrCallback(callback, err, operationID)
//		var result server_api_params.GetMessageListReactionExtensionsResp
//		extendMsgs, _ := c.db.GetMultipleMessageReactionExtension(msgIDList)
//		for _, v := range extendMsgs {
//			var singleResult server_api_params.SingleMessageExtensionResult
//			temp := make(map[string]*server_api_params.KeyValue)
//			_ = json.Unmarshal(v.LocalReactionExtensions, &temp)
//			for s, _ := range temp {
//				if !utils.IsContain(s, keyList) {
//					delete(temp, s)
//				}
//			}
//			singleResult.ClientMsgID = v.ClientMsgID
//			singleResult.ReactionExtensionList = temp
//			result = append(result, &singleResult)
//		}
//		args := syncReactionExtensionParams{}
//		args.MessageList = localMessageList
//		args.SourceID = sourceID
//		args.TypeKeyList = keyList
//		args.SessionType = sessionType
//		args.ExtendMessageList = extendMsgs
//		args.IsExternalExtension = isExternalExtension
//		_ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
//			OperationID: operationID,
//			Action:      constant.SyncMessageListReactionExtensions,
//			Args:        args,
//		}, c.GetCh())
//		return result
//	}
//
//	funcation (c *Conversation) setTypeKeyInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, typeKey, ex string, isCanRepeat bool, operationID string) []*server_api_params.ExtensionResult {
//		message, err := c.db.GetMessageController(s)
//		common.CheckDBErrCallback(callback, err, operationID)
//		if message.Status != constant.MsgStatusSendSuccess {
//			common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
//		}
//		extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
//		temp := make(map[string]*server_api_params.KeyValue)
//		_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
//		var flag bool
//		var isContainSelfK string
//		var dbIsCanRepeat bool
//		var deletedKeyValue server_api_params.KeyValue
//		var maxTypeKey string
//		var maxTypeKeyValue server_api_params.KeyValue
//		reqTemp := make(map[string]*server_api_params.KeyValue)
//		for k, v := range temp {
//			if strings.HasPrefix(k, typeKey) {
//				flag = true
//				singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//				_ = json.Unmarshal([]byte(v.Value), singleTypeKeyInfo)
//				if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
//					isContainSelfK = k
//					dbIsCanRepeat = singleTypeKeyInfo.IsCanRepeat
//					delete(singleTypeKeyInfo.InfoList, c.loginUserID)
//					singleTypeKeyInfo.Counter--
//					deletedKeyValue.TypeKey = v.TypeKey
//					deletedKeyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
//					deletedKeyValue.LatestUpdateTime = v.LatestUpdateTime
//				}
//				if k > maxTypeKey {
//					maxTypeKey = k
//					maxTypeKeyValue = *v
//				}
//			}
//		}
//		if !flag {
//			if len(temp) >= 300 {
//				common.CheckAnyErrCallback(callback, 202, errors.New("number of keys only can support 300"), operationID)
//			}
//			singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//			singleTypeKeyInfo.TypeKey = getIndexTypeKey(typeKey, 0)
//			singleTypeKeyInfo.Counter = 1
//			singleTypeKeyInfo.IsCanRepeat = isCanRepeat
//			singleTypeKeyInfo.Index = 0
//			userInfo := new(sdk.Info)
//			userInfo.UserID = c.loginUserID
//			userInfo.Ex = ex
//			singleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
//			keyValue := new(server_api_params.KeyValue)
//			keyValue.TypeKey = singleTypeKeyInfo.TypeKey
//			keyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
//			reqTemp[singleTypeKeyInfo.TypeKey] = keyValue
//		} else {
//			if isContainSelfK != "" && !dbIsCanRepeat {
//				//删除操作
//				reqTemp[isContainSelfK] = &deletedKeyValue
//			} else {
//				singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//				_ = json.Unmarshal([]byte(maxTypeKeyValue.Value), singleTypeKeyInfo)
//				userInfo := new(sdk.Info)
//				userInfo.UserID = c.loginUserID
//				userInfo.Ex = ex
//				singleTypeKeyInfo.Counter++
//				singleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
//				maxTypeKeyValue.Value = utils.StructToJsonString(singleTypeKeyInfo)
//				data, _ := json.Marshal(maxTypeKeyValue)
//				if len(data) > 1000 { //单key超过了1kb
//					if len(temp) >= 300 {
//						common.CheckAnyErrCallback(callback, 202, errors.New("number of keys only can support 300"), operationID)
//					}
//					newSingleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//					newSingleTypeKeyInfo.TypeKey = getIndexTypeKey(typeKey, singleTypeKeyInfo.Index+1)
//					newSingleTypeKeyInfo.Counter = 1
//					newSingleTypeKeyInfo.IsCanRepeat = singleTypeKeyInfo.IsCanRepeat
//					newSingleTypeKeyInfo.Index = singleTypeKeyInfo.Index + 1
//					userInfo := new(sdk.Info)
//					userInfo.UserID = c.loginUserID
//					userInfo.Ex = ex
//					newSingleTypeKeyInfo.InfoList[c.loginUserID] = userInfo
//					keyValue := new(server_api_params.KeyValue)
//					keyValue.TypeKey = newSingleTypeKeyInfo.TypeKey
//					keyValue.Value = utils.StructToJsonString(newSingleTypeKeyInfo)
//					reqTemp[singleTypeKeyInfo.TypeKey] = keyValue
//				} else {
//					reqTemp[maxTypeKey] = &maxTypeKeyValue
//				}
//
//			}
//		}
//		var sourceID string
//		switch message.SessionType {
//		case constant.SingleChatType:
//			sourceID = message.SendID + message.RecvID
//		case constant.NotificationChatType:
//			sourceID = message.RecvID
//		case constant.GroupChatType, constant.SuperGroupChatType:
//			sourceID = message.RecvID
//		}
//		var apiReq server_api_params.SetMessageReactionExtensionsReq
//		apiReq.IsReact = message.IsReact
//		apiReq.ClientMsgID = message.ClientMsgID
//		apiReq.SourceID = sourceID
//		apiReq.SessionType = message.SessionType
//		apiReq.IsExternalExtensions = message.IsExternalExtensions
//		apiReq.ReactionExtensionList = reqTemp
//		apiReq.OperationID = operationID
//		apiReq.MsgFirstModifyTime = message.MsgFirstModifyTime
//		var apiResp server_api_params.SetMessageReactionExtensionsResp
//		c.p.PostFatalCallback(callback, constant.SetMessageReactionExtensionsRouter, apiReq, &apiResp.ApiResult, apiReq.OperationID)
//		var msg model_struct.LocalChatLogReactionExtensions
//		msg.ClientMsgID = message.ClientMsgID
//		resultKeyMap := make(map[string]*server_api_params.KeyValue)
//		for _, v := range apiResp.ApiResult.Result {
//			if v.ErrCode == 0 {
//				temp := new(server_api_params.KeyValue)
//				temp.TypeKey = v.TypeKey
//				temp.Value = v.Value
//				temp.LatestUpdateTime = v.LatestUpdateTime
//				resultKeyMap[v.TypeKey] = temp
//			}
//		}
//		err = c.db.GetAndUpdateMessageReactionExtension(message.ClientMsgID, resultKeyMap)
//		if err != nil {
//			log.Error(operationID, "GetAndUpdateMessageReactionExtension err:", err.Error())
//		}
//		if !message.IsReact {
//			message.IsReact = apiResp.ApiResult.IsReact
//			message.MsgFirstModifyTime = apiResp.ApiResult.MsgFirstModifyTime
//			err = c.db.UpdateMessageController(message)
//			if err != nil {
//				log.Error(operationID, "UpdateMessageController err:", err.Error(), message)
//
//			}
//		}
//		return apiResp.ApiResult.Result
//	}
//
//	funcation getIndexTypeKey(typeKey string, index int) string {
//		return typeKey + "$" + utils.IntToString(index)
//	}
func getPrefixTypeKey(typeKey string) string {
	list := strings.Split(typeKey, "$")
	if len(list) > 0 {
		return list[0]
	}
	return ""
}

//funcation (c *Conversation) getTypeKeyListInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, keyList []string, operationID string) (result []*sdk.SingleTypeKeyInfoSum) {
//	message, err := c.db.GetMessageController(s)
//	common.CheckDBErrCallback(callback, err, operationID)
//	if message.Status != constant.MsgStatusSendSuccess {
//		common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
//	}
//	if !message.IsReact {
//		common.CheckAnyErrCallback(callback, 202, errors.New("can get message reaction ex"), operationID)
//	}
//	extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
//	temp := make(map[string]*server_api_params.KeyValue)
//	_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
//	for _, v := range keyList {
//		singleResult := new(sdk.SingleTypeKeyInfoSum)
//		singleResult.TypeKey = v
//		for typeKey, value := range temp {
//			if strings.HasPrefix(typeKey, v) {
//				singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//				_ = json.Unmarshal([]byte(value.Value), singleTypeKeyInfo)
//				if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
//					singleResult.IsContainSelf = true
//				}
//				for _, info := range singleTypeKeyInfo.InfoList {
//					v := *info
//					singleResult.InfoList = append(singleResult.InfoList, &v)
//				}
//				singleResult.Counter += singleTypeKeyInfo.Counter
//			}
//		}
//		result = append(result, singleResult)
//	}
//	messageList := []*sdk_struct.MsgStruct{s}
//	_ = common.TriggerCmdSyncReactionExtensions(common.SyncReactionExtensionsNode{
//		OperationID: operationID,
//		Action:      constant.SyncMessageListTypeKeyInfo,
//		Args:        messageList,
//	}, c.GetCh())
//
//	return result
//}
//
//funcation (c *Conversation) getAllTypeKeyInfo(callback open_im_sdk_callback.Base, s *sdk_struct.MsgStruct, operationID string) (result []*sdk.SingleTypeKeyInfoSum) {
//	message, err := c.db.GetMessageController(s)
//	common.CheckDBErrCallback(callback, err, operationID)
//	if message.Status != constant.MsgStatusSendSuccess {
//		common.CheckAnyErrCallback(callback, 201, errors.New("only send success message can modify reaction extensions"), operationID)
//	}
//	if !message.IsReact {
//		common.CheckAnyErrCallback(callback, 202, errors.New("can get message reaction ex"), operationID)
//	}
//	extendMsg, _ := c.db.GetMessageReactionExtension(message.ClientMsgID)
//	temp := make(map[string]*server_api_params.KeyValue)
//	_ = json.Unmarshal(extendMsg.LocalReactionExtensions, &temp)
//	mapResult := make(map[string]*sdk.SingleTypeKeyInfoSum)
//	for typeKey, value := range temp {
//		singleTypeKeyInfo := new(sdk.SingleTypeKeyInfo)
//		err := json.Unmarshal([]byte(value.Value), singleTypeKeyInfo)
//		if err != nil {
//			log.Warn(operationID, "not this type ", value.Value)
//			continue
//		}
//		prefixKey := getPrefixTypeKey(typeKey)
//		if v, ok := mapResult[prefixKey]; ok {
//			for _, info := range singleTypeKeyInfo.InfoList {
//				t := *info
//				v.InfoList = append(v.InfoList, &t)
//			}
//			if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
//				v.IsContainSelf = true
//			}
//			v.Counter += singleTypeKeyInfo.Counter
//		} else {
//			v := new(sdk.SingleTypeKeyInfoSum)
//			v.TypeKey = prefixKey
//			v.Counter = singleTypeKeyInfo.Counter
//			for _, info := range singleTypeKeyInfo.InfoList {
//				t := *info
//				v.InfoList = append(v.InfoList, &t)
//			}
//			if _, ok := singleTypeKeyInfo.InfoList[c.loginUserID]; ok {
//				v.IsContainSelf = true
//			}
//			mapResult[prefixKey] = v
//		}
//	}
//	for _, v := range mapResult {
//		result = append(result, v)
//
//	}
//	return result
//}
