package com.wecloud.im.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.wecloud.dispatch.extend.ActionRequest;
import com.wecloud.im.entity.ImApiMessageOnlineSend;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.entity.ImMessage;
import com.wecloud.im.entity.ImMessageOnlineSend;
import com.wecloud.im.enums.ChatTypeEnum;
import com.wecloud.im.mapper.ImMessageMapper;
import com.wecloud.im.param.ChatContentVo;
import com.wecloud.im.param.GetReadersParam;
import com.wecloud.im.param.ImClientSimpleDto;
import com.wecloud.im.param.ImHistoryMessagePageParam;
import com.wecloud.im.param.MsgDeleteParam;
import com.wecloud.im.param.add.ImMsgRecall;
import com.wecloud.im.param.add.ImMsgSendToOnlineClient;
import com.wecloud.im.param.add.ImMsgUpdate;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.thousandchat.service.ThousandChatService;
import com.wecloud.im.vo.ImMessageOfflineListVo;
import com.wecloud.im.vo.OfflineMsgDto;
import com.wecloud.im.vo.ReaderList;
import com.wecloud.im.vo.ReaderVo;
import com.wecloud.im.ws.enums.MsgTypeEnum;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.model.WsResponse;
import com.wecloud.im.ws.model.request.PushVO;
import com.wecloud.im.ws.model.request.ReceiveDataVO;
import com.wecloud.im.ws.model.request.ReceiveVO;
import com.wecloud.im.ws.sender.AsyncPush;
import com.wecloud.im.ws.sender.ChannelSender;
import com.wecloud.utils.JsonUtils;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息存储表 服务实现类
 *
 * @author wei
 * @since 2021-04-29
 */
@Slf4j
@Service
public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMessage> implements ImMessageService {

    @Autowired
    private ImMessageMapper imMessageMapper;

    @Autowired
    private ImClientService imClientService;

    @Autowired
    private ImConversationService imConversationService;

    @Autowired
    private AsyncPush asyncPush;

    @Autowired
    private ImApplicationService imApplicationService;


    @Autowired
    private ImConversationMembersService imConversationMembersService;

    @Autowired
    private ChannelSender channelSender;

    @Autowired
    private ThousandChatService thousandChatService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Boolean> restApiImMessageSend(ImMsgSendToOnlineClient imMsgSendToOnlineClient, ImApplication imApplication) {


        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(
                new QueryWrapper<ImConversationMembers>().lambda()
                        .eq(ImConversationMembers::getFkConversationId, imMsgSendToOnlineClient.getConversationId())
        );
        if (membersList.isEmpty()) {
            log.info("membersList为空,toConversationId:" + imMsgSendToOnlineClient.getConversationId());
            return ApiResult.fail();

        }

        ImApiMessageOnlineSend imApiMessageOnlineSend = new ImApiMessageOnlineSend();
        imApiMessageOnlineSend.setCreateTime(new Date());

        String attributes = JsonUtils.encodeJson(imMsgSendToOnlineClient.getContent());
        imApiMessageOnlineSend.setContent(attributes);

        imApiMessageOnlineSend.setConversationId(imMsgSendToOnlineClient.getConversationId());


        // 遍历发送
        for (ImConversationMembers conversationMembers : membersList) {

            // 查询接收方
            ImClient imClientReceiver = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
                    .eq(ImClient::getFkAppid, imApplication.getId())
                    .eq(ImClient::getId, conversationMembers.getFkClientId()));
            if (imClientReceiver == null) {
                continue;
            }

            WsResponse<ImApiMessageOnlineSend> responseModel = new WsResponse<>();
            responseModel.setCmd(WsResponseCmdEnum.REST_API_MSG.getCmdCode());
            ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
            responseModel.setCode(result.getCode());
            responseModel.setMsg(result.getMessage());
            responseModel.setData(imApiMessageOnlineSend);
            responseModel.setReqId(null);

            //  向接收方推送
            channelSender.sendMsg(responseModel, imClientReceiver.getId());

        }


        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ImMessage saveImMessage(ImApplication imApplication, ImClient imClientSender, long messageId, ReceiveVO receiveVO, ReceiveDataVO sysParam) {

        ImMessage imMessage = new ImMessage();

        //  数据库字段类型为JSON格式
        //  因mysql关系型数据库非MongoDB文档类型数据库,第三方应用拓展的自定义参数名和值需使用json格式落库
        String contentJsonString = JsonUtils.encodeJson(receiveVO.getData());
        imMessage.setContent(contentJsonString);

        imMessage.setId(messageId);
        imMessage.setCreateTime(new Date());
        imMessage.setFkAppid(imApplication.getId());
        imMessage.setSender(imClientSender.getId());
        imMessage.setWithdraw(false);
        imMessage.setEvent(false);
        imMessage.setSystemFlag(false);
        imMessage.setSendStatus(2);
        imMessage.setMsgType(sysParam.getType());
        imMessage.setFkConversationId(sysParam.getToConversation());
        this.save(imMessage);
        return imMessage;
    }

    @Override
    public ImMessage saveImMessage(ImClientSimpleDto client, long messageId, ChatContentVo data) {

        ImMessage imMessage = new ImMessage();

        //  数据库字段类型为JSON格式
        //  因mysql关系型数据库非MongoDB文档类型数据库,第三方应用拓展的自定义参数名和值需使用json格式落库
        String contentJsonString = JsonUtils.encodeJson(data);
        imMessage.setContent(contentJsonString);

        imMessage.setId(messageId);
        imMessage.setCreateTime(new Date());
        imMessage.setFkAppid(client.getFkAppid());
        imMessage.setSender(client.getId());
        imMessage.setWithdraw(false);
        imMessage.setEvent(false);
        imMessage.setSystemFlag(false);
        imMessage.setSendStatus(2);
        imMessage.setMsgType(data.getType());
        imMessage.setFkConversationId(data.getToConversation());
        this.save(imMessage);
        return imMessage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean withdrawMsg(ActionRequest request, ImMsgRecall imMsgRecall) {
        ImClient imClientSender;
        ImApplication imApplication;
        if (request != null) {
            imClientSender = imClientService.getCacheImClient(request.getSenderClientId());
            imApplication = imApplicationService.getCacheById(imClientSender.getFkAppid());
        } else {
            imClientSender = imClientService.getCurentClient();
            // 查询imApplication
            imApplication = imApplicationService.getCacheById(imClientSender.getFkAppid());
        }
        if (imClientSender == null) {
            throw new BusinessException("查无发送用户信息");
        }
        if (imApplication == null) {
            throw new BusinessException("查无应用信息");
        }

        ImMessage messageById = this.getById(imMsgRecall.getMsgId());
        // 判断该消息是否是该客户端发送 todo 单向撤回、双向撤回开关可配置
        Boolean withdrawOther = true;
        if (!withdrawOther && !messageById.getSender().equals(imClientSender.getId())) {
            throw new BusinessException("不可撤回别人发送的消息");
        }
        if (messageById.getWithdraw()) {
            return Boolean.TRUE;
        }
        // 修改消息体
        messageById.setWithdraw(Boolean.TRUE);
        messageById.setWithdrawTime(new Date());
        // 清空消息
        messageById.setContent("{}");
        boolean saveOk = this.updateById(messageById);
        ImClient msgOwner = imClientService.getCacheImClient(messageById.getSender());
        if (msgOwner == null) {
            throw new BusinessException("未查找到消息发送者");
        }

        if (saveOk) {
            // 查询该会话所有成员
            List<ImConversationMembers> membersList = imConversationMembersService.list(
                    new QueryWrapper<ImConversationMembers>().lambda()
                            .eq(ImConversationMembers::getFkConversationId, messageById.getFkConversationId())
                            .notIn(ImConversationMembers::getFkClientId, imClientSender.getId())
            );
            if (membersList.isEmpty()) {
                log.info("membersList为空,toConversationId:" + messageById.getFkConversationId());
                throw new BusinessException("该会话成员列表为空");
            }
            // 遍历发送
            for (ImConversationMembers conversationMembers : membersList) {
//                // 保存收件箱
//                long imInboxId = SnowflakeUtil.getId();
//                ImInbox imInbox = new ImInbox();
//                imInbox.setId(imInboxId);
//                imInbox.setCreateTime(new Date());
//                imInbox.setFkAppid(imApplication.getId());
//                imInbox.setReceiver(conversationMembers.getFkClientId());
//                imInbox.setFkMsgId(messageId);
//                imInbox.setReadMsgStatus(0);
//                imInbox.setReceiverMsgStatus(0);
//                imInbox.setFkConversationId(toConversationId);
//                imInboxService.save(imInbox);

                // 查询接收方
                ImClient imClientReceiver = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
                        .eq(ImClient::getFkAppid, imApplication.getId())
                        .eq(ImClient::getId, conversationMembers.getFkClientId()));
                if (imClientReceiver == null) {
                    continue;
                }

                // 封装响应的实体
                ImMessageOnlineSend imMessageOnlineSend = new ImMessageOnlineSend();
                BeanUtils.copyProperties(messageById, imMessageOnlineSend);
                imMessageOnlineSend.setType(MsgTypeEnum.MSG_WITHDRAW.getUriCode());
                imMessageOnlineSend.setMsgId(messageById.getId());
                imMessageOnlineSend.setCreateTime(new Date());
                imMessageOnlineSend.setSender(imClientSender.getClientId());
                Map<String, Object> contentMap = Maps.newHashMap();
                contentMap.put("msgOwner", msgOwner.getClientId());
                imMessageOnlineSend.setContent(contentMap);
                imMessageOnlineSend.setConversationId(conversationMembers.getFkConversationId());
                imMessageOnlineSend.setWithdraw(Boolean.TRUE);
                imMessageOnlineSend.setEvent(Boolean.TRUE);

                //  向接收方推送
                WsResponse<ImMessageOnlineSend> responseModel = new WsResponse<>();
                responseModel.setCmd(WsResponseCmdEnum.ONLINE_EVENT_MSG.getCmdCode());
                ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
                responseModel.setCode(result.getCode());
                responseModel.setMsg(result.getMessage());
                responseModel.setData(imMessageOnlineSend);
                responseModel.setReqId(null);
                channelSender.sendMsg(responseModel, imClientReceiver.getId());

                // 获取自定义推送字段
                PushVO pushVO = imMsgRecall.getPush();

                // 异步推送系统通知消息
                asyncPush.push(pushVO, imClientReceiver, imApplication);
            }

            return Boolean.TRUE;

        } else {
            return Boolean.TRUE;
        }
    }

    @Override
    public Boolean deleteMsg(ActionRequest request, MsgDeleteParam param) {
        ImClient imClientSender;
        ImApplication imApplication;
        if (request != null) {
            imClientSender = imClientService.getCacheImClient(request.getSenderClientId());
            imApplication = imApplicationService.getCacheById(imClientSender.getFkAppid());
        } else {
            imClientSender = imClientService.getCurentClient();
            // 查询imApplication
            imApplication = imApplicationService.getCacheById(imClientSender.getFkAppid());
        }
        if (imClientSender == null) {
            throw new BusinessException("查无发送用户信息");
        }
        if (imApplication == null) {
            throw new BusinessException("查无应用信息");
        }
        // todo 单向撤回、双向撤回开关可配置
        Boolean deleteOther = true;
        List<ImMessage> imMessageList = this.listByIds(param.getMsgIds());
        if (CollectionUtils.isEmpty(imMessageList)) {
            throw new BusinessException("查无消息");
        }
        for (ImMessage message : imMessageList) {
            // 判断该消息是否是该客户端发送
            if (!deleteOther && !message.getSender().equals(imClientSender.getId())) {
                throw new BusinessException("不可删除别人发送的消息");
            }
            if (message.getIsDelete() == 2) {
                continue;
            }
            // 修改消息体
            message.setIsDelete(2);
            message.setUpdateDate(new Date());
            boolean saveOk = this.updateById(message);
            ImClient msgOwner = imClientService.getCacheImClient(message.getSender());
            if (msgOwner == null) {
                throw new BusinessException("未查找到消息发送者");
            }

            if (saveOk) {
                // 查询该会话所有成员
                List<ImConversationMembers> membersList = imConversationMembersService.list(
                        new QueryWrapper<ImConversationMembers>().lambda()
                                .eq(ImConversationMembers::getFkConversationId, message.getFkConversationId())
                                .notIn(ImConversationMembers::getFkClientId, imClientSender.getId())
                );
                if (membersList.isEmpty()) {
                    log.info("membersList为空,toConversationId:" + message.getFkConversationId());
                    throw new BusinessException("该会话成员列表为空");
                }
                // 遍历发送
                for (ImConversationMembers conversationMembers : membersList) {
//                // 保存收件箱
//                long imInboxId = SnowflakeUtil.getId();
//                ImInbox imInbox = new ImInbox();
//                imInbox.setId(imInboxId);
//                imInbox.setCreateTime(new Date());
//                imInbox.setFkAppid(imApplication.getId());
//                imInbox.setReceiver(conversationMembers.getFkClientId());
//                imInbox.setFkMsgId(messageId);
//                imInbox.setReadMsgStatus(0);
//                imInbox.setReceiverMsgStatus(0);
//                imInbox.setFkConversationId(toConversationId);
//                imInboxService.save(imInbox);
                    // 查询接收方
                    ImClient imClientReceiver = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
                            .eq(ImClient::getFkAppid, imApplication.getId())
                            .eq(ImClient::getId, conversationMembers.getFkClientId()));
                    if (imClientReceiver == null) {
                        continue;
                    }

                    // 封装响应的实体
                    ImMessageOnlineSend imMessageOnlineSend = new ImMessageOnlineSend();
                    BeanUtils.copyProperties(message, imMessageOnlineSend);
                    imMessageOnlineSend.setType(MsgTypeEnum.MSG_DELETE.getUriCode());
                    imMessageOnlineSend.setMsgId(message.getId());
                    imMessageOnlineSend.setCreateTime(new Date());
                    imMessageOnlineSend.setSender(imClientSender.getClientId());
                    Map<String, Object> contentMap = Maps.newHashMap();
                    contentMap.put("msgOwner", msgOwner.getClientId());
                    imMessageOnlineSend.setContent(contentMap);
                    imMessageOnlineSend.setConversationId(conversationMembers.getFkConversationId());
                    imMessageOnlineSend.setEvent(Boolean.TRUE);

                    //  向接收方推送
                    WsResponse<ImMessageOnlineSend> responseModel = new WsResponse<>();
                    responseModel.setCmd(WsResponseCmdEnum.ONLINE_EVENT_MSG.getCmdCode());
                    ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
                    responseModel.setCode(result.getCode());
                    responseModel.setMsg(result.getMessage());
                    responseModel.setData(imMessageOnlineSend);
                    responseModel.setReqId(null);
                    channelSender.sendMsg(responseModel, imClientReceiver.getId());
                }
                return Boolean.TRUE;
            } else {
                throw new BusinessException("删除消息错误，稍后重试");
            }
        }

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateMsgById(ImMsgUpdate imMsgUpdate) {

//        ImClient client = imClientService.getCurentClient();

/*        // 判断该消息是否是该客户端发送
        ImMessage messageById = this.getById(imMsgUpdate.getId());
        if (!messageById.getSender().equals(client.getId())) {
            log.error("判断该消息是否是该客户端发送");
            return ApiResult.fail();
        }*/

        ImMessage imMessage = new ImMessage();
        imMessage.setId(imMsgUpdate.getMsgId());

        String content = JsonUtils.encodeJson(imMsgUpdate.getContent());
        imMessage.setContent(content);

        if (this.updateById(imMessage)) {
            return Boolean.TRUE;
        } else {
            throw new BusinessException("修改消息错误");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveImMessage(ImMessage imMessage) throws Exception {
        return super.save(imMessage);
    }

    /**
     * 获取读取人员
     *
     * @param param
     * @return
     */
    @Override
    public ReaderList getReaders(ActionRequest request, GetReadersParam param) {
        ImClient imClientSender;
        if (request != null) {
            imClientSender = imClientService.getCacheImClient(request.getSenderClientId());
        } else {
            imClientSender = imClientService.getCurentClient();
        }
        if (imClientSender == null) {
            throw new BusinessException("查无发送用户信息");
        }
        List<ReaderVo> readerVos = imMessageMapper.getReaders(imClientSender.getId(), param);
        if (CollectionUtils.isEmpty(readerVos)) {
            return null;
        }
        List<ReaderVo> readList = Lists.newArrayList();
        List<ReaderVo> unReadList = Lists.newArrayList();
        for (ReaderVo readerVo : readerVos) {
            if (readerVo.getReadMsgStatus() == 0) {
                unReadList.add(readerVo);
            } else {
                readList.add(readerVo);
            }
        }
        ReaderList readerList = new ReaderList();
        readerList.setReadList(readList);
        readerList.setUnReadList(unReadList);
        return readerList;
    }

    @Override
    public Paging<OfflineMsgDto> getHistoryMsgConversationId(ImHistoryMessagePageParam imHistoryMessagePageParam) {
        Page<ImMessage> page = new PageInfo<>(imHistoryMessagePageParam, OrderItem.desc(getLambdaColumn(ImMessage::getCreateTime)));

        IPage<OfflineMsgDto> iPage = imMessageMapper.getHistoryMsgConversationId(page, imHistoryMessagePageParam);

        return new Paging<>(iPage);
    }


    /**
     * 查询用户所有离线消息
     *
     * @return
     * @throws Exception
     */
    @Override
    public List<ImMessageOfflineListVo> getOfflineList() throws Exception {

        // 返回数据
        List<ImMessageOfflineListVo> imMessageOfflineListVoList = new ArrayList<>();

        ImClient client = imClientService.getCurentClient();

        // 获取加入的所有会话
        List<ImConversation> myImConversationList = imConversationService.getMyImConversationList();

        List<ImConversation> thousandConversations = new ArrayList<>();
        // 遍历会话列表, 查询每个会话列表的离线消息
        for (ImConversation imConversation : myImConversationList) {

            //万人群 暂时跳过，后面统一处理
            if (ChatTypeEnum.THOUSAND_GROUP.getCode().equals(imConversation.getChatType())) {
                thousandConversations.add(imConversation);
                continue;
            }

            //根据客户端id与会话id 查询离线消息
            List<OfflineMsgDto> offlineListByClientAndConversation = this.getOfflineListByClientAndConversation(client.getId(), imConversation.getId());

            // 房间消息为空则不添加
            if (offlineListByClientAndConversation.isEmpty()) {
                continue;
            }
            ImMessageOfflineListVo imMessageOfflineListVo = new ImMessageOfflineListVo();
            imMessageOfflineListVo.setMsgList(offlineListByClientAndConversation);
            imMessageOfflineListVo.setConversationId(imConversation.getId());
            imMessageOfflineListVoList.add(imMessageOfflineListVo);
        }
        // 万人群的部分
        if (CollectionUtils.isNotEmpty(thousandConversations)) {
            List<ImMessageOfflineListVo> offlineMsgs = thousandChatService.findOfflineMsgs(thousandConversations);
            imMessageOfflineListVoList.addAll(offlineMsgs);
        }

        return imMessageOfflineListVoList;
    }

    /**
     * 根据客户端id与会话id 查询离线消息
     *
     * @param clientId
     * @param conversationId
     * @return
     */
    @Override
    public List<OfflineMsgDto> getOfflineListByClientAndConversation(Long clientId, Long conversationId) {
        return imMessageMapper.getOfflineListByClientAndConversation(clientId, conversationId);
    }


    @Override
    public OfflineMsgDto getReceivedLastMsgByConversationId(Long clientId, Long conversationId) {
        return imMessageMapper.getReceivedLastMsgByConversationId(clientId, conversationId);
    }

    @Override
    public OfflineMsgDto getLastMsgByConversationId(Long conversationId) {
        return imMessageMapper.getLastMsgByConversationId(conversationId);
    }


}
