package com.cm.websocket;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.cm.websocket.enums.AppSocketTypeEnum;
import com.cm.websocket.enums.SendStatusEnum;
import com.cm.websocket.exception.*;
import com.cm.websocket.handler.TextSendHandler;
import com.cm.websocket.manager.AppSocketSessionManager;
import com.cm.websocket.pojo.AppSocketMessage;
import com.cm.websocket.pojo.AppSocketSession;
import com.cm.websocket.service.INoticeService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: WebSocket
 * @Description:
 * @Author: WangGeng
 * @Date: 2020/9/26 11:40
 * @Version: 1.0
 **/
@Component
@ServerEndpoint("/ws")
public class WebSocket {

    private static final Logger log = LoggerFactory.getLogger(WebSocket.class);
    private static ApplicationContext applicationContext;
    private ApplicationContext serviceContext = applicationContext;

    /**
     * 连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        log.debug("新客户端连接");
        AppSocketSessionManager.getInstance().addSession(session);
    }

    /**
     * 连接关闭
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        try {
            noticeOffline(session.getId());
            AppSocketSessionManager.getInstance().removeSessionById(session.getId());
            // 发送离线通知
            session.close();
        } catch (IOException | SessionException e) {
            e.printStackTrace();
        }
        log.debug("关闭一个客户端");
    }

    /**
     * 接收到消息
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMsg(String message, Session session) {
        System.out.println("message: " + message);
        AppSocketMessage.SendStatusBody sendStatusBody = null;
        AppSocketMessage appSocketMessage = null;
        try {
            appSocketMessage = JSONObject.parseObject(message, AppSocketMessage.class);
            if (appSocketMessage.getType() == null) {
                throw new TypeErrorException("Type类型不能为空");
            }
            if (StringUtils.isBlank(appSocketMessage.getBody())) {
                throw new BodyException("Body主体不能为空");
            }
            if (AppSocketTypeEnum.REGISTER.getValue() == appSocketMessage.getType()) {
                sendStatusBody = registerAppSession(session, appSocketMessage);
            } else if (AppSocketTypeEnum.MESSAGE.getValue() == appSocketMessage.getType()) {
                sendTextMessage(session, appSocketMessage);
            } else if (AppSocketTypeEnum.GROUP_MESSAGE.getValue() == appSocketMessage.getType()) {
                AppSocketSessionManager.getInstance().getMessageService().sendGroupTextMessage(session, appSocketMessage);
            } else if (AppSocketTypeEnum.UPDATE_SERVICE_HANDLE_STATUS.getValue() == appSocketMessage.getType()) {
                updateServiceHandleStatus(session, appSocketMessage);
            } else if (AppSocketTypeEnum.SEARCH_ONLINE_USER.getValue() == appSocketMessage.getType()) {
                listOnlineUser(session, appSocketMessage);
            } else if (AppSocketTypeEnum.SEARCH_ONLINE_USER_FRIEND.getValue() == appSocketMessage.getType()) {
                listOnlineUserFriend(session, appSocketMessage);
            } else if (AppSocketTypeEnum.SEARCH_COUNT_NEED_TO_DEALT_WITH.getValue() == appSocketMessage.getType()) {
                countNeedToDealWith(session, appSocketMessage);
            }
        } catch (SessionException e) {
            sendStatusBody = new AppSocketMessage.SendStatusBody(SendStatusEnum.SESSION_ERROR.getValue(), SendStatusEnum.SESSION_ERROR, e.getMessage());
        } catch (TypeErrorException e) {
            sendStatusBody = new AppSocketMessage.SendStatusBody(SendStatusEnum.SESSION_ERROR.getValue(), SendStatusEnum.SESSION_ERROR, e.getMessage());
        } catch (BodyException e) {
            sendStatusBody = new AppSocketMessage.SendStatusBody(SendStatusEnum.SESSION_ERROR.getValue(), SendStatusEnum.SESSION_ERROR, e.getMessage());
        } catch (UserErrorException e) {
            sendStatusBody = new AppSocketMessage.SendStatusBody(SendStatusEnum.TO_ERROR.getValue(), SendStatusEnum.SESSION_ERROR, e.getMessage());
        } catch (JSONException e) {
            appSocketMessage = new AppSocketMessage();
            appSocketMessage.setSystem(true);
            appSocketMessage.setFrom(AppSocketSessionManager.FORM_SYSTEM);
            sendStatusBody = new AppSocketMessage.SendStatusBody(SendStatusEnum.TO_ERROR.getValue(), SendStatusEnum.MESSAGE_ERROR, e.getMessage());
        } finally {
            if (sendStatusBody != null) {
                appSocketMessage.setBody(JSONObject.toJSONString(sendStatusBody));
                send(session, appSocketMessage);
            }
        }
    }

    /**
     * 更新业务处理状态并返回总数
     */
    private void updateServiceHandleStatus(Session session, AppSocketMessage appSocketMessage) {
        String userId = appSocketMessage.getFrom();
        appSocketMessage.setTo(userId);
        AppSocketMessage.CountNeedToDealWithBody countNeedToDealWithBody = JSONObject.parseObject(appSocketMessage.getBody(), AppSocketMessage.CountNeedToDealWithBody.class);
        INoticeService noticeService = serviceContext.getBean(INoticeService.class);
        noticeService.updateHandleStatus(userId, countNeedToDealWithBody);
        countNeedToDealWithBody.setServiceId(null);
        countNeedToDealWith(session, appSocketMessage, noticeService, countNeedToDealWithBody);
    }

    /**
     * 待办总数
     *
     * @param session
     * @param appSocketMessage
     */
    private void countNeedToDealWith(Session session, AppSocketMessage appSocketMessage) {
        String userId = appSocketMessage.getFrom();
        appSocketMessage.setTo(userId);
        AppSocketMessage.CountNeedToDealWithBody countNeedToDealWithBody = JSONObject.parseObject(appSocketMessage.getBody(), AppSocketMessage.CountNeedToDealWithBody.class);
        INoticeService noticeService = serviceContext.getBean(INoticeService.class);
        countNeedToDealWith(session, appSocketMessage, noticeService, countNeedToDealWithBody);
    }

    /**
     * 待办总数
     *
     * @param session
     * @param appSocketMessage
     * @param noticeService
     * @param countNeedToDealWithBody
     */
    private void countNeedToDealWith(Session session, AppSocketMessage appSocketMessage, INoticeService noticeService, AppSocketMessage.CountNeedToDealWithBody countNeedToDealWithBody) {
        appSocketMessage.setType(AppSocketTypeEnum.SEARCH_COUNT_NEED_TO_DEALT_WITH.getValue());
        List<AppSocketMessage.CountNeedToDealWithBody> counts = noticeService.listNoticeCount(appSocketMessage.getFrom(), countNeedToDealWithBody);
        appSocketMessage.setBody(JSONArray.toJSONString(counts));
        send(session, appSocketMessage);
    }

    /**
     * APP注册会话
     *
     * @param session
     * @param appSocketMessage
     * @return
     * @throws SessionException
     */
    private AppSocketMessage.SendStatusBody registerAppSession(Session session, AppSocketMessage appSocketMessage) throws SessionException {
        appSocketMessage.setSystem(true);
        appSocketMessage.setFrom(AppSocketSessionManager.FORM_SYSTEM);
        AppSocketMessage.RegisterBody registerBody = JSONObject.parseObject(appSocketMessage.getBody(), AppSocketMessage.RegisterBody.class);
        AppSocketSessionManager.getInstance().registerAppWebSocket(session.getId(), registerBody);
        // 通知在线
        noticeOnline(registerBody);
        return new AppSocketMessage.SendStatusBody(SendStatusEnum.SUCCESS.getValue(), SendStatusEnum.SUCCESS, "OK");
    }

    /**
     * 获取全部人员的在线状态
     *
     * @param session
     * @param appSocketMessage
     */
    public void listOnlineUser(Session session, AppSocketMessage appSocketMessage) {
        String userId = appSocketMessage.getBody();
        List<AppSocketSession> appSocketSessions = AppSocketSessionManager.getInstance().listSession();
        JSONArray userIdJSONArray = new JSONArray();
        for (AppSocketSession appSocketSession : appSocketSessions) {
            if (StringUtils.equals(userId, appSocketSession.getUserId())) {
                continue;
            }
            userIdJSONArray.add(appSocketSession.getUserId());
        }
        appSocketMessage.setSystem(true);
        appSocketMessage.setBody(JSONArray.toJSONString(userIdJSONArray));
        appSocketMessage.setFrom(AppSocketSessionManager.FORM_SYSTEM);
        appSocketMessage.setType(AppSocketTypeEnum.SEND_STATUS_ONLINE.getValue());
        send(session, appSocketMessage);
    }

    /**
     * 获取用户的朋友在线状态
     *
     * @param session
     * @param appSocketMessage
     */
    public void listOnlineUserFriend(Session session, AppSocketMessage appSocketMessage) {
        listOnlineUser(session, appSocketMessage);
    }

    /**
     * 通知在线，无用户信息，不进行通知
     *
     * @param registerBody
     */
    private void noticeOnline(AppSocketMessage.RegisterBody registerBody) {
        if (StringUtils.isBlank(registerBody.getUserId())) {
            return;
        }
        JSONArray userIdJSONArray = new JSONArray();
        userIdJSONArray.add(registerBody.getUserId());

        AppSocketMessage appSocketMessage = new AppSocketMessage();
        appSocketMessage.setSystem(true);
        appSocketMessage.setBody(JSONArray.toJSONString(userIdJSONArray));
        appSocketMessage.setFrom(AppSocketSessionManager.FORM_SYSTEM);
        appSocketMessage.setType(AppSocketTypeEnum.SEND_STATUS_ONLINE.getValue());
        sendOnlineOrOfflineMessage(appSocketMessage, registerBody.getUserId());
    }

    /**
     * 通知所有在线用户离线
     *
     * @param sessionId
     * @throws SessionException
     */
    private void noticeOffline(String sessionId) throws SessionException {
        AppSocketSession offlineSession = AppSocketSessionManager.getInstance().getSessionById(sessionId);
        String userId = offlineSession.getUserId();
        if (StringUtils.isBlank(userId)) {
            return;
        }

        JSONArray userIdJSONArray = new JSONArray();
        userIdJSONArray.add(userId);

        AppSocketMessage appSocketMessage = new AppSocketMessage();
        appSocketMessage.setSystem(true);
        appSocketMessage.setBody(JSONArray.toJSONString(userIdJSONArray));
        appSocketMessage.setFrom(AppSocketSessionManager.FORM_SYSTEM);
        appSocketMessage.setType(AppSocketTypeEnum.SEND_STATUS_OFFLINE.getValue());
        sendOnlineOrOfflineMessage(appSocketMessage, userId);
    }

    /**
     * 发送在线和离线信息
     *
     * @param appSocketMessage
     * @param userId
     */
    private void sendOnlineOrOfflineMessage(AppSocketMessage appSocketMessage, String userId) {
        List<AppSocketSession> appSocketSessions = AppSocketSessionManager.getInstance().listSession();
        for (AppSocketSession appSocketSession : appSocketSessions) {
            if (StringUtils.isBlank(appSocketSession.getUserId())) {
                continue;
            }
            if (StringUtils.equals(appSocketSession.getUserId(), userId)) {
                continue;
            }
            send(appSocketSession.getSession(), appSocketMessage);
        }
    }

    /**
     * 发送文字消息
     *
     * @param session
     * @param appSocketMessage
     * @throws UserErrorException
     */
    private void sendTextMessage(Session session, AppSocketMessage appSocketMessage) throws UserErrorException, UserSessionException {
        if (StringUtils.isBlank(appSocketMessage.getTo())) {
            throw new UserErrorException("To 值不能为空");
        }
        appSocketMessage.setSystem(false);
        List<AppSocketSession> appSocketSessions = AppSocketSessionManager.getInstance().listSessionByUserId(appSocketMessage.getTo());
        // 发送失败返回结果
        if (appSocketSessions.isEmpty()) {
            sendTextFailed(session, appSocketMessage, "用户不在线");
        }
        // 发送消息
        for (AppSocketSession appSocketSession : appSocketSessions) {
            sendText(appSocketSession.getSession(), session, appSocketMessage);
        }
    }

    public static void send(Session session, AppSocketMessage appSocketMessage) {
        session.getAsyncRemote().sendText(JSONObject.toJSONString(appSocketMessage));
    }

    public static void sendText(Session toSession, Session fromSession, AppSocketMessage appSocketMessage) {
        toSession.getAsyncRemote().sendText(JSONObject.toJSONString(appSocketMessage), new TextSendHandler(fromSession, appSocketMessage));
    }

    /**
     * 发送文本失败
     *
     * @param fromSession
     * @param appSocketMessage
     * @param msg
     */
    public static void sendTextFailed(Session fromSession, AppSocketMessage appSocketMessage, String msg) {
        appSocketMessage.setType(AppSocketTypeEnum.SEND_STATUS.getValue());
        appSocketMessage.setBody(JSONObject.toJSONString(new AppSocketMessage.SendStatusBody(SendStatusEnum.FAILED.getValue(), SendStatusEnum.FAILED, msg)));
        send(fromSession, appSocketMessage);
    }

    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocket.applicationContext = applicationContext;
    }
}
