package ink.wgink.module.instantmessage.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.user.IUserBaseService;
import ink.wgink.module.instantmessage.pojo.bos.NoticeBO;
import ink.wgink.module.instantmessage.pojo.bos.NoticeTargetBO;
import ink.wgink.module.instantmessage.pojo.pos.NoticePO;
import ink.wgink.module.instantmessage.pojo.vos.NoticeSendVO;
import ink.wgink.module.instantmessage.pojo.vos.NoticeVO;
import ink.wgink.module.instantmessage.service.IMessageService;
import ink.wgink.module.instantmessage.service.INoticeService;
import ink.wgink.module.instantmessage.websocket.enums.MessageTypeEnum;
import ink.wgink.module.instantmessage.websocket.manager.WebSocketChannelManager;
import ink.wgink.module.instantmessage.websocket.pojo.WebSocketClientMessage;
import ink.wgink.pojo.session.WebSocketSession;
import ink.wgink.module.instantmessage.websocket.pojo.body.CountNeedToDealWithBody;
import ink.wgink.module.instantmessage.websocket.pojo.body.NoticeBody;
import ink.wgink.pojo.dtos.user.UserDTO;
import ink.wgink.util.BeanPropertyCheckUtil;
import ink.wgink.util.UUIDUtil;
import io.netty.channel.Channel;
import org.apache.commons.compress.utils.Sets;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: MessageServiceImpl
 * @Description: 消息
 * @Author: wanggeng
 * @Date: 2021/1/14 3:28 下午
 * @Version: 1.0
 */
@Service
public class MessageServiceImpl extends DefaultBaseService implements IMessageService {

    @Autowired
    private INoticeService noticeService;
    @Autowired
    private IUserBaseService userBaseService;
    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);

    @Override
    public void notice(NoticeSendVO noticeSendVO) throws Exception {
        NoticeVO.Notice notice = new NoticeVO.Notice();
        notice.setTitle(noticeSendVO.getNoticeTitle());
        notice.setMsg(noticeSendVO.getNoticeMsg());
        notice.setServiceId("IM_NOTICE:" + UUIDUtil.getUUID());
        notice.setSystem("IM");
        notice.setTarget(noticeSendVO.getNoticeTarget());
        notice.setUserIds(Arrays.asList(noticeSendVO.getNoticeUserIds().split("\\_")));

        List<NoticeVO.Notice> notices = new ArrayList<>();
        notices.add(notice);
        NoticeVO noticeVO = new NoticeVO();
        noticeVO.setNotices(notices);
        // 发送消息
        saveNotice(noticeVO, true);
    }

    @Override
    public void notice(NoticeBO noticeBO) {
        notice(null, noticeBO);
    }

    @Override
    public void notice(String clientName, NoticeBO noticeBO) {
        List<WebSocketSession> webSocketSessions = listWebSocketSessions(clientName, noticeBO);
        NoticeBody noticeBody = new NoticeBody(noticeBO.getTitle(), noticeBO.getContent());
        WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage(MessageTypeEnum.NOTICE.getValue(),
                true,
                WebSocketChannelManager.FORM_SYSTEM,
                noticeBO.getTo(),
                JSONObject.toJSONString(noticeBody));
        notice(webSocketSessions, webSocketClientMessage);
    }

    @Override
    public void notice(NoticeTargetBO noticeTargetBO) {
        notice(null, noticeTargetBO);
    }

    @Override
    public void notice(String clientName, NoticeTargetBO noticeTargetBO) {
        List<WebSocketSession> webSocketSessions = listWebSocketSessions(clientName, noticeTargetBO);
        NoticeBody noticeBody = new NoticeBody(noticeTargetBO.getTitle(), noticeTargetBO.getContent(), noticeTargetBO.getTarget());
        WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage(MessageTypeEnum.NOTICE_TARGET_MESSAGE.getValue(),
                true,
                WebSocketChannelManager.FORM_SYSTEM,
                noticeTargetBO.getTo(),
                JSONObject.toJSONString(noticeBody));
        notice(webSocketSessions, webSocketClientMessage);
    }

    @Override
    public void saveNotice(NoticeVO noticeVO, boolean isSave) throws Exception {
        for (NoticeVO.Notice notice : noticeVO.getNotices()) {
            if (notice.getUserIds() == null || notice.getUserIds().isEmpty()) {
                continue;
            }
            if (StringUtils.isBlank(notice.getTitle())) {
                notice.setTitle("系统消息");
            }
            if (StringUtils.isBlank(notice.getMsg())) {
                notice.setMsg("您有一条系统消息");
            }

            List<WebSocketSession> webSocketSessions = WebSocketChannelManager.getInstance().listOnlineUser(notice.getUserIds());
            scheduledExecutorService.schedule(() -> {
                for (WebSocketSession webSocketSession : webSocketSessions) {
                    NoticeBody noticeBody = new NoticeBody(notice.getTitle(), notice.getMsg(), notice.getTarget());
                    WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage(MessageTypeEnum.NOTICE.getValue(),
                            true,
                            WebSocketChannelManager.FORM_SYSTEM,
                            webSocketSession.getUserId(),
                            JSONObject.toJSONString(noticeBody));
                    // 发送通知
                    WebSocketChannelManager.getInstance().sendText(webSocketSession.getChannel(), webSocketClientMessage);
                }
                if (isSave) {
                    try {
                        // 保存发送记录
                        List<String> userIds = notice.getUserIds();
                        List<UserDTO> userDTOs = userBaseService.listByUserIds(userIds);
                        for (UserDTO userDTO : userDTOs) {
                            boolean isOnline = false;
                            for (WebSocketSession webSocketSession : webSocketSessions) {
                                if (StringUtils.equals(userDTO.getUserId(), webSocketSession.getUserId())) {
                                    isOnline = true;
                                    break;
                                }
                            }
                            if (isOnline) {
                                noticeService.save(userDTO, notice, true);
                            } else {
                                noticeService.save(userDTO, notice, false);
                            }
                        }
                    } catch (Exception e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
            }, 2, TimeUnit.SECONDS);
        }
    }

    /**
     * 会话列表
     *
     * @param clientName
     * @param noticeBO
     * @return
     */
    private List<WebSocketSession> listWebSocketSessions(String clientName, NoticeBO noticeBO) {
        BeanPropertyCheckUtil.checkField(noticeBO);
        List<String> userIds = new ArrayList<>(Sets.newHashSet(noticeBO.getTo().split(",")));
        List<WebSocketSession> webSocketSessions;
        if (StringUtils.isBlank(clientName)) {
            webSocketSessions = WebSocketChannelManager.getInstance().listOnlineUser(userIds);
        } else {
            webSocketSessions = WebSocketChannelManager.getInstance().listOnlineUser(userIds, clientName);
        }
        if (webSocketSessions.isEmpty()) {
            throw new SearchException("用户不存在");
        }
        return webSocketSessions;
    }

    /**
     * 通知
     *
     * @param webSocketSessions
     * @param webSocketClientMessage
     */
    private void notice(List<WebSocketSession> webSocketSessions, WebSocketClientMessage webSocketClientMessage) {
        for (WebSocketSession webSocketSession : webSocketSessions) {
            WebSocketChannelManager.getInstance().sendText(webSocketSession.getChannel(), webSocketClientMessage);
        }
    }

    @Override
    public void delete(List<String> serviceIds) {
        List<NoticePO> noticePOs = noticeService.listPO(serviceIds);
        Set<String> noticeUserIdSet = new HashSet<>();
        noticePOs.forEach(noticePO -> {
            noticeUserIdSet.add(noticePO.getUserId());
        });
        // 删除通知
        noticeService.deleteByServiceIds(serviceIds);
        // 重新推送数量
        List<WebSocketSession> webSocketSessions = WebSocketChannelManager.getInstance().listOnlineUser(new ArrayList<>(noticeUserIdSet));
        // 3s后通知，保证事务提交
        scheduledExecutorService.schedule(() -> {
            // 更新状态待办
            for (WebSocketSession webSocketSession : webSocketSessions) {
                for (NoticePO noticePO : noticePOs) {
                    if (StringUtils.equals(webSocketSession.getUserId(), noticePO.getUserId())) {
                        Channel channel = webSocketSession.getChannel();
                        CountNeedToDealWithBody countNeedToDealWithBody = new CountNeedToDealWithBody();
                        countNeedToDealWithBody.setSystem(noticePO.getNoticeSystem());
                        // 查询待办状态
                        List<CountNeedToDealWithBody> counts = noticeService.listNoticeCount(webSocketSession.getUserId(), countNeedToDealWithBody);
                        WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage(MessageTypeEnum.SEARCH_COUNT_NEED_TO_DEALT_WITH.getValue(),
                                true,
                                WebSocketChannelManager.FORM_SYSTEM,
                                webSocketSession.getUserId(),
                                JSONArray.toJSONString(counts));
                        WebSocketChannelManager.getInstance().sendText(channel, webSocketClientMessage);
                        break;
                    }
                }

            }
        }, 3, TimeUnit.SECONDS);
    }

    @Override
    public void noticeUnSendByUserId(WebSocketSession webSocketSession) {
        List<NoticePO> noticePOs = noticeService.listPOByUserIdAndIsSend(webSocketSession.getUserId(), 0);
        if (noticePOs.isEmpty()) {
            return;
        }
        // 3s后发送通知
        scheduledExecutorService.schedule(() -> {
            if (webSocketSession.getChannel() == null || !webSocketSession.getChannel().isOpen() || !webSocketSession.getChannel().isActive()) {
                return;
            }
            noticePOs.forEach(noticePO -> {
                NoticeBody noticeBody = new NoticeBody(noticePO.getNoticeTitle(), noticePO.getNoticeMsg());
                WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage(MessageTypeEnum.NOTICE.getValue(),
                        true,
                        WebSocketChannelManager.FORM_SYSTEM,
                        webSocketSession.getUserId(),
                        JSONObject.toJSONString(noticeBody));
                WebSocketChannelManager.getInstance().sendText(webSocketSession.getChannel(), webSocketClientMessage);
                // 更新发送状态
                noticeService.updateSendStatus(noticePO.getNoticeId(), 1);
            });
        }, 3, TimeUnit.SECONDS);
    }
}
