package com.tuoheng.admin.websocket;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tuoheng.admin.constant.WebSocketConstant;
import com.tuoheng.admin.entity.Accident;
import com.tuoheng.admin.entity.User;
import com.tuoheng.admin.enums.AccidentStatusEnum;
import com.tuoheng.admin.enums.MarkEnum;
import com.tuoheng.admin.mapper.AccidentMapper;
import com.tuoheng.admin.mapper.UserMapper;
import com.tuoheng.admin.request.accident.QueryAccidentListRequest;
import com.tuoheng.admin.service.accident.build.BuildAccidentVoListService;
import com.tuoheng.admin.vo.accident.AccidentVo;
import com.tuoheng.admin.websocket.listener.RedisMessageListener;
import com.tuoheng.common.core.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
@ServerEndpoint("/webSocket/{sid}")
public class WebSocketServer {

    // 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    public static AtomicInteger onlineNum = new AtomicInteger();

    // concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketServer对象。
    public static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来与客户端进行数据收发
     */
    private Session session;

    /**
     * 用来引入刚才在webcoketConfig注入的类
     */
    private RedisMessageListenerContainer container = SpringUtils.getBean("redisMessageListenerContainer");

    private UserMapper userMapper = SpringUtils.getBean("userMapper");
    
    private AccidentMapper accidentMapper = SpringUtils.getBean("accidentMapper");

    private BuildAccidentVoListService buildAccidentVoListService = SpringUtils.getBean("buildAccidentVoListService");

    /**
     * 自定义的消息发送器
     */
    private RedisMessageListener listener;

    /**
     * 发送消息
     *
     * @param session
     * @param message
     * @throws IOException
     */
    public void sendMessage(Session session, String message) throws IOException {
        if (session != null) {
            synchronized (session) {
//                log.info("发送数据：" + message);
                session.getBasicRemote().sendText(message);
            }
        }
    }

    /**
     * 给指定用户发送信息
     *
     * @param sid
     * @param message
     */
    public void sendInfo(String sid, String message) {
        Session session = sessionPools.get(sid);
        try {
            this.sendMessage(session, message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 建立连接成功调用
     *
     * @param session
     * @param sid
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "sid") String sid) {
        Session sessionTmp = sessionPools.get(sid);
        if (ObjectUtil.isEmpty(sessionTmp)) {
            // 在线人数增加1
            this.addOnlineCount();

            String message = "欢迎" + (sid + "&" + session.getId()) + "加入连接！当前在线人数：" + onlineNum;
            log.info(message);

            this.session = session;
            sessionPools.put(sid, session);

            listener = new RedisMessageListener();
            // 放入session
            listener.setSession(session);
            container.addMessageListener(listener, new PatternTopic(WebSocketConstant.TOPIC_ACCIDENT));
        } else {
            log.info("该用户已加入，sid={}", sid);
            this.session = session;
        }

        // 首次连接，给客户端推送数据
        this.accidentListNotice(sid);
    }

    /**
     * 关闭连接时调用
     *
     * @param sid
     * @param session
     */
    @OnClose
    public void onClose(@PathParam(value = "sid") String sid, Session session) {
        try {
            sessionPools.remove(sid);
            this.subOnlineCount();
            log.info("{} 断开webSocket连接！当前人数为:{}", sid, onlineNum);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 收到客户端信息
     *
     * @param message
     * @param sid
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "sid") String sid) throws IOException {
        //message = "客户端：" + message + ",已收到";
        log.info("客户端：" + message + "，已收到");
        for (Session session : sessionPools.values()) {
            log.info("session：{}", session);
        }
        try {
            //回复客户端
            //sendMessage(session, message);

            //发送到服务端
//            DroneControlClient droneControlClient = new DroneControlClient();
//            droneControlClient.sendMd(message, sid);
            //sendMessage(session, message);
        } catch (Exception e) {
            e.printStackTrace();
            //continue;
        }
    }

    /**
     * 错误时调用
     *
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.info("发生错误, throwable={}", throwable);
        throwable.printStackTrace();
    }

    public static void addOnlineCount() {
        onlineNum.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

    private void accidentListNotice(String sid) {
        String[] sidArr = sid.split(":");
        String username = sidArr[0];
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, username)
                .eq(User::getMark, MarkEnum.VALID.getCode()));
        if (ObjectUtil.isEmpty(user)) {
            log.info("该用户不存在，sid={}", sid);
            return;
        }
        QueryAccidentListRequest request = new QueryAccidentListRequest();
        List<Integer> statusList = new ArrayList<>();
        statusList.add(AccidentStatusEnum.UNTREATED.getCode());
        statusList.add(AccidentStatusEnum.PROCESSING.getCode());
        statusList.add(AccidentStatusEnum.PROCESSED.getCode());
        request.setStatusList(statusList);
        request.setTenantId(user.getTenantId());
        List<Accident> accidentList = accidentMapper.selectAccidentList(request);

        // 构造返回结果对象
        List<AccidentVo> accidentVoList = buildAccidentVoListService.build(accidentList);
        if (CollectionUtil.isNotEmpty(accidentVoList)) {
            String json = JSONObject.toJSONString(accidentVoList);
            this.sendInfo(sid, json);
        }
    }

}
