package com.tourism.webadmin.ws.handle;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.tourism.webadmin.app.website.dto.MessageDTO;
import com.tourism.webadmin.app.website.service.TourMemberService;
import com.tourism.webadmin.back.dao.TourImMemberMapper;
import com.tourism.webadmin.back.model.*;
import com.tourism.webadmin.back.model.constant.IsTrue;
import com.tourism.webadmin.back.service.*;
import com.tourism.webadmin.enums.MessageLimitType;
import com.tourism.webadmin.enums.NoticeType;
import com.tourism.webadmin.ws.service.IMessageService;
import com.tourism.webadmin.ws.service.impl.IGroupMessageServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Component
public class SystemMessageHandler extends AbstractWebSocketHandler {

    private ApplicationContext applicationContext;

    private TourImGroupService tourImGroupService;

    private TourImMemberService tourImMemberService;

    private TourUserService tourUserService;

    private TourFansService tourFansService;

    private TourMemberService tourMemberService;

    private TourImMessageService tourImMessageService;

    private TourImMemberMapper tourImMemberMapper;

//    private AESUtils aesUtil;

    private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();

    private static Map<String,Map<String,WebSocketSession>> groupMap = new ConcurrentHashMap<>();

    @Autowired
    private SensitiveWordBs sensitiveWordBs;


    public SystemMessageHandler(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
//        this.aesUtil = applicationContext.getBean(AESUtils.class);
        this.tourMemberService= applicationContext.getBean(TourMemberService.class);
        this.tourUserService = applicationContext.getBean(TourUserService.class);
        this.tourImGroupService = applicationContext.getBean(TourImGroupService.class);
        this.tourImMemberService = applicationContext.getBean(TourImMemberService.class);
        this.tourFansService = applicationContext.getBean(TourFansService.class);
        this.tourImMemberMapper = applicationContext.getBean(TourImMemberMapper.class);
        this.tourImMessageService = applicationContext.getBean(TourImMessageService.class);
        List<TourImGroup> imGroupList = this.tourImGroupService.list();
        if (!imGroupList.isEmpty()) {
            for (TourImGroup tourImGroup : imGroupList) {
                groupMap.put(tourImGroup.getId().toString(),new ConcurrentHashMap<String,WebSocketSession>());
            }
        }
    }

    public static Map<String, Map<String, WebSocketSession>> getGroupMap() {
        return groupMap;
    }

    public static void setGroupMap(Map<String, Map<String, WebSocketSession>> groupMap) {
        SystemMessageHandler.groupMap = groupMap;
    }

    public static Map<String, WebSocketSession> getSessionMap() {
        return sessionMap;
    }

    public static void setSessionMap(Map<String, WebSocketSession> sessionMap) {
        SystemMessageHandler.sessionMap = sessionMap;
    }

    /**
     * 连接时调用方法
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        Object userId = attributes.get("userId");
        if(userId != null) {
            //不存在才保持连接 不然不需要保持连接
            if (!sessionMap.containsKey(userId.toString())) {
                sessionMap.put(userId.toString(), session);
            }
        }
        //这一块来处理我们一个加入群聊连接
        List<TourImMember> list = tourImMemberService.list(new LambdaQueryWrapper<TourImMember>().eq(TourImMember::getUserId, userId));
        if (list.isEmpty()) {
            return;
        }
        Set<Long> groupIds = list.stream().map(TourImMember::getGroupId).collect(Collectors.toSet());
        for (Long aLong : groupIds) {
            if (groupMap.containsKey(aLong.toString())) {
                groupMap.get(aLong.toString()).put(userId.toString(), session);
            }
        }
    }


    /**
     * 连接出错时调用
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        exception.printStackTrace();
        Map<String, Object> attributes = session.getAttributes();
        Object userId = attributes.get("userId");
        if(userId != null) {
            if (sessionMap.containsKey(userId.toString())) {
                sessionMap.remove(userId.toString());
            }
            List<TourImMember> tourImMembers = tourImMemberService.list(new LambdaQueryWrapper<TourImMember>().eq(TourImMember::getUserId, userId));
            Set<Long> collect = tourImMembers.stream().map(TourImMember::getGroupId).collect(Collectors.toSet());
            for (Long aLong : collect) {
                if (groupMap.containsKey(aLong.toString())){
                    Map<String, WebSocketSession> stringWebSocketSessionMap = groupMap.get(aLong.toString());
                    if(stringWebSocketSessionMap!=null){
                        stringWebSocketSessionMap.remove(userId);
                    }
                }
            }

        }

    }

    /**
     * 关闭连接时调用
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        Object userId = attributes.get("userId");
        if(userId != null) {
            if (sessionMap.containsKey(userId.toString())) {
                sessionMap.remove(userId.toString());
            }
            List<TourImMember> tourImMembers = tourImMemberService.list(new LambdaQueryWrapper<TourImMember>().eq(TourImMember::getUserId, userId));
            Set<Long> collect = tourImMembers.stream().map(TourImMember::getGroupId).collect(Collectors.toSet());
            for (Long aLong : collect) {
                if (groupMap.containsKey(aLong.toString())){
                    Map<String, WebSocketSession> stringWebSocketSessionMap = groupMap.get(aLong.toString());
                    if(stringWebSocketSessionMap!=null){
                        stringWebSocketSessionMap.remove(userId);
                    }
                }
            }
        }

    }

    /**
     * 心跳
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        session.sendMessage(message);
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        //登录用户ID
        String id = attributes.get("userId").toString();
        String payload = message.getPayload();
        if (StringUtils.isBlank(payload)) {
            return;
        }
        MessageDTO messageDTO = JSON.parseObject(payload, MessageDTO.class);

        //单聊 对方ID为空
        if(NoticeType.SYSTEM.getCode().equals(messageDTO.getNoticeType())&&ObjectUtil.isEmpty(messageDTO.getGetUserId())) {
            return;
        }

        //发送人
        TourUser my = tourUserService.getById(Long.valueOf(id));
        if (my==null) return;

        // 单聊 必须有 接收人
        TourUser to=null;
        if((NoticeType.SYSTEM.getCode().equals(messageDTO.getNoticeType()))){
             //接收人
             to = tourUserService.getById(Long.valueOf(messageDTO.getGetUserId()));
            if(to==null){
                return;
            }
        }
        //群聊ID
        TourImGroup tourImGroup = tourImGroupService.getById(messageDTO.getGroupId());

        //当前的会话是否存在
        List<TourImMember> ls = tourImMemberService.lambdaQuery().eq(TourImMember::getGroupId, tourImGroup.getId())
                .eq(TourImMember::getUserId, id)
                .list();
        if(ObjectUtil.isEmpty(ls)){
            //不存在
            return;
        }

        // 修改会话为显示
        LambdaQueryWrapper<TourImMember> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TourImMember::getGroupId, tourImGroup.getId())
                .eq(TourImMember::getIsShow, IsTrue.FALSE);
//                .eq(TourImMember::getUserId, id);
        TourImMember entity = new TourImMember();
        entity.setIsShow(IsTrue.TRUE);
        tourImMemberMapper.update(entity,queryWrapper);

        //判断 对方 是否创建了群聊
        List<TourImMember> list = tourImMemberService.lambdaQuery().eq(TourImMember::getGroupId,tourImGroup.getId())
                .eq(TourImMember::getUserId, messageDTO.getGetUserId())
                .eq(TourImMember::getPartnerId, id).list();

        //对方没有会话 且 是单聊
        if (ObjectUtil.isEmpty(list) && NoticeType.SYSTEM.getCode().equals(messageDTO.getNoticeType())) {
            //新增群组成功后新增回话
            TourImMember tourImMember = new TourImMember();
            tourImMember.setGroupId(tourImGroup.getId());
            tourImMember.setGroupBackImage("");

            tourImMember.setUserId(Long.valueOf(messageDTO.getGetUserId()));
            tourImMember.setCreateUserId(my.getUserId());
            tourImMember.setCreateTime(new Date());
            tourImMember.setUpdateTime(new Date());
            tourImMember.setPartnerId(my.getUserId());
            tourImMember.setUpdateUserId(my.getUserId());
            tourImMember.setGroupRole(1);
            //新增自己的会话
            tourImMemberService.save(tourImMember);
            //把新群组发送人和接收人放入房间
            ConcurrentHashMap<String, WebSocketSession> stringWebSocketSessionConcurrentHashMap = new ConcurrentHashMap<>();
            //自己的
            stringWebSocketSessionConcurrentHashMap.put(id, session);
            //判断对方是否存在连接
            String getUserId = messageDTO.getGetUserId();
            if (sessionMap.containsKey(getUserId)) {
                stringWebSocketSessionConcurrentHashMap.put(getUserId,sessionMap.get(getUserId));
            }
            groupMap.put(tourImGroup.getId().toString(),stringWebSocketSessionConcurrentHashMap);
            //带回给前端
            messageDTO.setGroupId(tourImGroup.getId().toString());
        }else {
            /**
             * 发消息限制
             * 0不限制
             * 1首发消息限制  对方关注过自己之前 或者自己已经发过一条消息且对方回复消息之前
             * 2踢出群聊发消息限制
             * 3群解散发消息限制
             * 4封禁群聊发消息限制
             */
            //1首发消息限制
            if (messageDTO.getNoticeType()==1){
                //  对方关注自己之前
                List<TourFans> fansList = tourFansService.list(new LambdaQueryWrapper<TourFans>()
                        .eq(TourFans::getAttentionId,my.getUserId())//被关注人是自己
                        .eq(TourFans::getCreateUserId, to.getUserId())//关注人是对方
                        .in(TourFans::getDeleteFlag,1,-1));
                //在已经创建会话并且 自己 已经发送过一条消息 对方回复消息之前
                long myMessageCount = 0;
                long toMessageCount = 0;
                if (NoticeType.SYSTEM.getCode().equals(messageDTO.getNoticeType())) {
                    myMessageCount = tourImMessageService.count(new LambdaQueryWrapper<TourImMessage>()
                            .eq(TourImMessage::getGroupId, messageDTO.getGroupId())
                            .eq(TourImMessage::getCreateUserId, my.getUserId())
                    );
                    toMessageCount = tourImMessageService.count(new LambdaQueryWrapper<TourImMessage>()
                            .eq(TourImMessage::getGroupId, messageDTO.getGroupId())
                            .eq(TourImMessage::getCreateUserId, to.getUserId())
                    );
                }
                if (toMessageCount == 0 && myMessageCount >= 1 && fansList.isEmpty()) {
                    messageDTO.setMessageLimit(MessageLimitType.MESSAGE_LIMIT_FIRST_MESSAGE.getCode());
                }
            }


            long count = tourMemberService.count(new LambdaQueryWrapper<TourImMember>()//2已被踢出群聊
                    .eq(TourImMember::getGroupId, tourImGroup.getId())
                    .eq(TourImMember::getUserId, id)
                    .eq(TourImMember::getDataState,1));

            if (tourImGroup.getDataState() ==-1){
                messageDTO.setMessageLimit(MessageLimitType.MESSAGE_LIMIT_DISSOLVE.getCode()); //3群被解散
            } else if (tourImGroup.getBannedStatus()==0){
                messageDTO.setMessageLimit(MessageLimitType.MESSAGE_LIMIT_BANNED.getCode());//4群被封禁
            } else if (count == 0) {
                messageDTO.setMessageLimit(MessageLimitType.MESSAGE_LIMIT_KICK_OUT.getCode());//4被踢出群聊
            }
            if (messageDTO.getMessageLimit()!=0) {
                sendMessage(JSON.toJSONString(messageDTO),sessionMap.get(id));
                return;
            }
        }

        messageDTO.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        //1.群聊 2.单聊
        if ((Objects.equals(messageDTO.getNoticeType(), NoticeType.GROUP.getCode()) || (Objects.equals(messageDTO.getNoticeType(), NoticeType.SYSTEM.getCode()))) && Objects.equals(messageDTO.getMessageType(), 0) &&!StringUtils.isBlank(messageDTO.getMessageContent())){
            String replace = sensitiveWordBs.replace(messageDTO.getMessageContent());
            messageDTO.setMessageContent(replace);
        }
        //先处理群聊
        IMessageService messageService = new IGroupMessageServiceImpl(applicationContext);
        if (NoticeType.GROUP.getCode().equals(messageDTO.getNoticeType())){
            sendGroupAllMessage(messageDTO.getGroupId(),new TextMessage(JSON.toJSONString(messageDTO)));
            messageDTO.setSendUserId(id);
            messageService.save(messageDTO);
            return;
        }
        //单聊
        if (groupMap.containsKey(messageDTO.getGroupId())) {
            //在线情况
            //不在线情况 直接入库 默认全未读
            sendGroupAllMessage(messageDTO.getGroupId(),new TextMessage(JSON.toJSONString(messageDTO)));
            Map<String, WebSocketSession> groupSingleMessageMap = groupMap.get(messageDTO.getGroupId());
            String getUserId = messageDTO.getGetUserId();
            messageDTO.setSendUserId(id);
            messageService.save(messageDTO);
            return;
        }
    }

    /**判断是否已经存在该会话*/

    public String getGroupId(Long userId,Long partnerId) {
        LambdaQueryWrapper<TourImMember> query = new LambdaQueryWrapper<>();
        query.and(
                qw -> qw.eq(TourImMember::getUserId, userId)
                        .eq(TourImMember::getPartnerId, partnerId))
                    .or(
                qw -> qw.eq(TourImMember::getUserId, partnerId)
                        .eq(TourImMember::getPartnerId, userId));
        List<TourImMember> member = tourImMemberService.list(query);
        if(member.size()<2){
            return null;
        }
        return member.get(0).getGroupId().toString();
    }
    /**
     * 群聊消息发送
     * @param textMessage
     */
    public void sendGroupAllMessage(String groupId,TextMessage textMessage) {
        if (StringUtils.isBlank(groupId)) {
            return;
        }
        if (groupMap.containsKey(groupId)) {
            Map<String, WebSocketSession> userWebsocketSession = groupMap.get(groupId);
            userWebsocketSession.forEach((key,value)->{
                if (value.isOpen()) {
                    try {
                        value.sendMessage(textMessage);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            return;
        }
        return;
    }

    /**
     * 单聊
     * @param textMessage
     * @param session
     */
    public void sendSingleMessage(TextMessage textMessage,WebSocketSession session) {
        if (session.isOpen()) {
            try {
                session.sendMessage(textMessage);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }



    /**
     * 发送系统消息给所有人系统消息
     * @param message
     * @throws Exception
     */
    public void sendAllMessage(TextMessage message) throws Exception {
        sessionMap.forEach( (key,value) ->{
            if (value.isOpen()) {
                try {
                    value.sendMessage(message);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    /**
     * 给单个人系统发送消息
     * @param message
     */
    public void sendMessage(String message,WebSocketSession session) throws Exception {
        if (session.isOpen()) {
            session.sendMessage(new TextMessage(message));
        }
    }


}
