package com.cn.service;

import com.alibaba.fastjson.JSONObject;
import org.jim.common.ImAio;
import org.jim.common.ImConfig;
import org.jim.common.message.MessageHelper;
import org.jim.common.utils.JsonKit;
import org.jim.server.command.CommandManager;
import org.jim.server.model.*;
import nl.basjes.shaded.org.springframework.util.CollectionUtils;
import org.jim.server.enums.*;
import org.apache.commons.lang3.StringUtils;
import org.jim.common.ImConst;
import org.jim.common.ImStatus;
import org.jim.common.cache.redis.JedisTemplate;
import org.jim.common.packets.*;
import org.jim.server.command.handler.processor.login.LoginCmdProcessor;
import org.jim.server.util.SnowflakeIdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.utils.lock.SetWithLock;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Darren
 * @date 2020/2/2 11:03
 */
public class IMChatLoginServiceProcessor implements LoginCmdProcessor {

    private static Logger logger = LoggerFactory.getLogger(IMChatLoginServiceProcessor.class);

    private static JedisTemplate jedisTemplate = null;

    protected static ImConfig imConfig;

    private static final String GROUP_PROPERTY = " * ";

    private static final String CURRENT_SYSTEM_ALL_USER_ID = "CURRENT_SYSTEM_ALL_USER_ID";

    static {
        try {
            imConfig = CommandManager.getImConfig();
            jedisTemplate = JedisTemplate.me();
        } catch (Exception e) {
            logger.info("JedisTemplate 和 imConfig 初始化失败!");
            e.printStackTrace();
        }
    }

    /**
     * doLogin方法注意：J-IM登陆命令是根据user是否为空判断是否登陆成功,
     * 当登陆失败时设置user属性需要为空，相反登陆成功user属性是必须非空的;
     * @param loginReqBody
     * @param channelContext
     * @return
     */
    @Override
    public LoginRespBody doLogin(LoginReqBody loginReqBody, ChannelContext channelContext) {
        logger.info("执行登录处理器doLogin()方法...");
        //获取token
        String token = loginReqBody.getToken();
        LoginRespBody loginRespBody;
        User user = null;
        if(StringUtils.isNotBlank(token)){
            user = this.getUser(token);
        }
        if(user == null){
            loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10008);
        }else{
            Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
            if(noOnlineService != null && noOnlineService){
                loginRespBody =  new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10022,user);
            }else{
                loginRespBody = new LoginRespBody(Command.COMMAND_LOGIN_RESP,ImStatus.C10007,user);
                String userJson = JsonKit.toJSONString(user);
                Map<String,String> users = jedisTemplate.hashGetAll(CURRENT_SYSTEM_ALL_USER_ID);
                if(!CollectionUtils.isEmpty(users)){
                    boolean flag = true;
                    for(Map.Entry<String,String> entry : users.entrySet()){
                        if(entry.getKey() == user.getId() ){
                            flag = false;
                        }
                    }
                    if(flag){
                        //存储所有登录的用户 的 userId
                        jedisTemplate.hashSet(CURRENT_SYSTEM_ALL_USER_ID, user.getId(), userJson);
                    }
                }else{
                    jedisTemplate.hashSet(CURRENT_SYSTEM_ALL_USER_ID, user.getId(), userJson);
                }
            }
        }
        return loginRespBody;
    }

    /**
     * 通过token 获取用户信息
     * @param token
     * @return {@link {@link User}}
     * @author Darren
     * @date 2020/2/5 15:34
     */
    public User getUser(String token) {
        User user = null;
        String[] tokenArray = token.split(ImConst.SEPARATOR);
        if(tokenArray.length == 2){
            //判断访问类型
            String visitType = tokenArray[0];
            Map<String, String> map = jedisTemplate.hashGetAll(token);
            if( !CollectionUtils.isEmpty(map) ){
                if( VisitTypeEnum.SERVICEACCOUNT.getKey().equals(visitType) ){
                    user = this.createServiceAccountUser(map);
                    this.disposeServiceAccountData(user);
                }
                if( VisitTypeEnum.CUSTOMER.getKey().equals(visitType) ){
                    //当前访问人为客户
                    user = this.createCustomerData(map);
                }
                if( VisitTypeEnum.VISITOR.getKey().equals(visitType) ){
                    //当前访问人为游客
                    user = this.createVisitorData(map);
                    //修改游客在线状态
                    Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
                    if(!noOnlineService){
                        VisitorDepartmentMiddle.dao.findById(user.getId()).set("online",0).update();
                    }
                }
            }
        }
        return user;
    }

    /** ============================================= <游客> 处理 start================================================== */

    /**
     * 创建 游客 user 数据
     * @param map
     * @return {@link {@link User}}
     * @author Darren
     * @date 2020/2/5 15:30
     */
    private User createVisitorData(Map<String, String> map) {
        User user = new User();
        user.setId(map.get("visitorDepartmentId"));
        String headImg = map.get("headImg") == null ? null : map.get("headImg").replace("\"","");
        user.setAvatar(headImg);
        String nickname = map.get("nickname") == null ? null : map.get("nickname").replace("\"","");
        user.setNick(nickname);
        Map<String, Object> visitorMap = new HashMap<>();
        visitorMap.put("visitorId",map.get("id"));
        visitorMap.put("companyId",map.get("companyId"));
        visitorMap.put("departmentId",map.get("departmentId"));
        visitorMap.put("phone",map.get("phone") == null ? null : map.get("phone").replace("\"",""));
        user.setExtras(new JSONObject(visitorMap));
        //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
        user.setVisitType(VisitTypeEnum.VISITOR.getKey());
        user.setGroups(this.initVisitorGroups(user));
        return user;
    }

    /**
     * 初始化游客 群组数据
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/5 15:26
     */
    private List<Group> initVisitorGroups(User user) {
        List<Group> groups = disposeVisitorGroupData(user);
        Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
        if(!noOnlineService){
            this.createConversation(groups.get(0),user);
        }
        return groups;
    }

    /**
     * 处理游客群组数据
     * 自动分配原则，根据上次服务状态，优先分配上次服务客服，
     * 若上次服务客服不在线或不存在上次服务客服状况，
     * 则优先根据当前在线客服服务人数最少的客服进行分配，和服务上限无关；
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/5 15:25
     */
    private List<Group> disposeVisitorGroupData(User user){
        List<Group> groups = new ArrayList<>();

        //获取 当前平台下 部门下 所有的在线的客服
        String serviceAccountContainerKey
                =  user.getExtras().getString("companyId") + ImConst.SEPARATOR
                + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
                + VisitTypeEnum.SERVICEACCOUNT.getKey() ;
        //value组成 : Map<String ( serviceAccountRoleDepartmentId ), String (当前连接客户人数)>
        Map<String, String> serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey);

        StringBuilder sql1 = new StringBuilder();
        sql1.append(" select ")
                .append(GROUP_PROPERTY)
                .append(" from sw_group where 1 = 1 ")
                .append(" and company_id = " + user.getExtras().getString("companyId"))
                .append(" and department_id = " + user.getExtras().getString("departmentId"))
                .append(" and consumer_id = " + user.getId())
                .append(" order by create_time desc");
        //判断 游客在平台下部门下 是否有群组 且判断 游客对应的客服 是否在线
        ChatGroup chatGroup1 = ChatGroup.dao.findFirst(sql1.toString());
        boolean isOnline = false;
        if( null != chatGroup1 ){
            String accountRoleDepartmentId = chatGroup1.getStr("service_account_role_department_middle_id");
            //游客对应客服存在 且 在线; isOnline=true;
            isOnline = serviceAccountContainer.containsKey(accountRoleDepartmentId);
        }
        user.getExtras().put("noOnlineService",false);
        if(!isOnline){
            // 从当前在线的客服中 获取 连接客户数最少的 客服
            Map.Entry<String, String> minReceptionNumServiceAccount = this.getServiceAccountMinReceptionNum(serviceAccountContainer);
            if( null != minReceptionNumServiceAccount ){
                //当前平台下部门下 存在客服
                user.getExtras().put("serviceAccountRoleDepartmentId",minReceptionNumServiceAccount.getKey());
                String sql2 = this.createSelectGroupSql(user);
                ChatGroup chatGroup2 = ChatGroup.dao.findFirst(sql2);
                if( null != chatGroup2){
                    chatGroup2
                            // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
                            .set("group_state",GroupStateEnum.ON_LINE.getKey())
                            // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
                            .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
                            // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
                            .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                            // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
                            .set("message_state",MessageStateEnum.NO.getKey())
                            .update();
                    //之前 客服 和 客户/游客 之间 有群组
                    Group group = disposeChatGroupToGroup(chatGroup2);
                    groups.add(group);
                }else{
                    //创建群组
                    Group group = this.createGroup(user);
                    //把 Group 转成 Map 用于 jfianl框架的 添加使用
                    Map<String, Object> groupAttrs = groupToMap(group);
                    groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
                    groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
                    //客服是否是离线状态 1: 是 0: 否
                    groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
                    //群组留言状态 1:留言 0:非留言
                    groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
                    //jfianl框架的 保存
                    ChatGroup.dao._setAttrs(groupAttrs).save();
                    groups.add(group);
                }
                //更改 客服正在 服务的 客户/游客 数量
                int num = Integer.valueOf(minReceptionNumServiceAccount.getValue());
                jedisTemplate.hashSet(serviceAccountContainerKey,minReceptionNumServiceAccount.getKey(),String.valueOf(++num));
            }else{
                //当前平台下部门下 没有在线客服
                user.getExtras().put("noOnlineService",true);
            }
        }else{
            Group group = disposeChatGroupToGroup(chatGroup1);
            groups.add(group);
            chatGroup1
                    // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
                    .set("group_state",GroupStateEnum.ON_LINE.getKey())
                    // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
                    .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
                    // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
                    .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                    // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
                    .set("message_state",MessageStateEnum.NO.getKey())
                    .update();
        }

        return groups;
    }

    /** ============================================= <游客> 处理 end================================================== */

    /** ============================================= <客户> 处理 start================================================== */

    /**
     * 创建 客户 user 数据
     * @param map
     * @return {@link {@link User}}
     * @author Darren
     * @date 2020/2/4 10:07
     */
    private User createCustomerData(Map<String, String> map) {
        User user = new User();
        user.setId(map.get("customerDepartmentId"));
        String headImg = map.get("headImg") == null ? null : map.get("headImg").replace("\"","");
        user.setAvatar(headImg);
        String nickname = map.get("nickname") == null ? null : map.get("nickname").replace("\"","");
        user.setNick(nickname);
        Map<String, Object> customerMap = new HashMap<>();
        customerMap.put("isBlack",map.get("isBlack"));
        customerMap.put("isClaim",map.get("isClaim"));
        customerMap.put("customerId",map.get("id"));
        customerMap.put("companyId",map.get("companyId"));
        customerMap.put("departmentId",map.get("departmentId"));
        customerMap.put("serviceAccountId",map.get("serviceAccountId"));
        customerMap.put("phone",map.get("phone") == null ? null : map.get("phone").replace("\"",""));
        customerMap.put("serviceAccountRoleDepartmentId",map.get("serviceAccountRoleDepartmentId"));
        user.setExtras(new JSONObject(customerMap));
        //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
        user.setVisitType(VisitTypeEnum.CUSTOMER.getKey());
        user.setGroups(this.initCustomerGroups(user));
        return user;
    }

    /**
     * 绑定客户群组 一个客户在一个平台下在一个部门下 只会有一个群组
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/4 11:05
     */
    private List<Group> initCustomerGroups(User user) {
        //获取 当前平台下 部门下 在线的 客服
        String serviceAccountContainerKey
                =  user.getExtras().getString("companyId") + ImConst.SEPARATOR
                + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
                + VisitTypeEnum.SERVICEACCOUNT.getKey() ;

        //是否创建 会话
        user.getExtras().put("isCreateConversation",true);

        List<Group> groups = new ArrayList<>();
        // isClaim: 是否认领 1:是 0:否; isBlack: 是否加入黑名单 1:是 0:否;
        if(
                CustomerClaimTypeEnum.YES.getKey().equals(user.getExtras().getString("isClaim")) &&
                        BlackListStateEnum.NO.getKey().equals(user.getExtras().getString("isBlack"))
        ){
            //被认领 并且 没有加入黑名单
            String receptionNum
                    = jedisTemplate.hashGet(
                    serviceAccountContainerKey
                    , user.getExtras().getString("serviceAccountRoleDepartmentId")
            );
            String sql = this.createSelectGroupSql(user);
            ChatGroup chatGroup = ChatGroup.dao.findFirst(sql);
            if( null != chatGroup){
                //判断  (之前群组 是否是留言群组 )  >>> 现在 处理的是 客户登录的处理逻辑
                if(
                    MessageStateEnum.YES.getKey() == chatGroup.getInt("message_state")
                ){
                    // 群组 是留言群组 则不重新创建会话
                    user.getExtras().put("isCreateConversation",false);
                }
                chatGroup
                        // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
                        .set("group_state",GroupStateEnum.ON_LINE.getKey())
                        // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
                        .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
                        .update();
                //判断当前 认领这个 客户 的客服 是否在线
                if( StringUtils.isNotBlank(receptionNum) ){
                    //在线
                    chatGroup
                            // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
                            .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                            // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
                            .set("message_state",MessageStateEnum.NO.getKey())
                            .update();
                    //更改客服的服务数量
                    Integer num = Integer.valueOf(receptionNum);
                    jedisTemplate.hashSet(serviceAccountContainerKey,user.getExtras().getString("serviceAccountRoleDepartmentId"),String.valueOf(++num));
                }else{
                    //离线
                    chatGroup
                            // 更改 sw_group 表中 service_account_type 字段为 离线 (客服离线状态 1: 离线 0: 在线)
                            .set("service_account_type",ServiceAccountOfflineTypeEnum.OFF_LINE.getKey())
                            // 更改 sw_group 表中 message_state 字段为 留言 (群组留言状态 1:留言 0:非留言)
                            .set("message_state",MessageStateEnum.YES.getKey())
                            .update();
                }
                //之前 客服 和 客户 之间 有群组
                Group group = this.disposeChatGroupToGroup(chatGroup);
                groups.add(group);
            }else{
                // 创建新的群组
                Group group = this.createGroup(user);
                //把 Group 转成 Map 用于 jfianl框架的 添加使用
                Map<String, Object> groupAttrs = this.groupToMap(group);
                groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
                groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
                //判断当前 认领这个 客户 的客服 是否在线
                if( StringUtils.isNotBlank(receptionNum) ){
                    //在线
                    //更改客服的服务数量
                    Integer num = Integer.valueOf(receptionNum);
                    jedisTemplate.hashSet(serviceAccountContainerKey,user.getExtras().getString("serviceAccountRoleDepartmentId"),String.valueOf(++num));
                    //客服是否是离线状态 1: 是 0: 否
                    groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
                    //群组留言状态 1:留言 0:非留言
                    groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
                }else{
                    //离线
                    //客服是否是离线状态 1: 是 0: 否
                    groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.OFF_LINE.getKey());
                    //群组留言状态 1:留言 0:非留言
                    groupAttrs.put("message_state", MessageStateEnum.YES.getKey());
                }
                //jfianl框架的 保存
                ChatGroup.dao._setAttrs(groupAttrs).save();
                groups.add(group);
            }

        }else{
            //按照游客 方式 处理 找到 在线客服 连接数 最少的
            groups.addAll(this.disposeVisitorGroupData(user));
        }
        if( user.getExtras().getBoolean("isCreateConversation")){
            Boolean noOnlineService = user.getExtras().getBoolean("noOnlineService");
            if(!noOnlineService){
                this.createConversation(groups.get(0),user);
            }
        }
        return groups;
    }

    /**
     * 创建 sw_conversation 数据
     * @param group
     * @param user
     * @return {@link }
     * @author Darren
     * @date 2020/2/6 10:16
     */
    private void createConversation(Group group,User user) {
        Map<String, Object> attrs = new HashMap<>();
        Long conversationId = SnowflakeIdUtils.getInstance().nextId();
        attrs.put("id", conversationId );
        //会话状态 0：游客会话 1：客户会话  群组类型 0:游客-客服 类型 1:客户-客服 类型
        attrs.put("state", group.getGroupType());
        if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){
            attrs.put("user_id", Long.valueOf(user.getExtras().getString("customerId")));
        }
        if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){
            attrs.put("user_id", Long.valueOf(user.getExtras().getString("visitorId")));
        }
        attrs.put("user_nickname", user.getNick());
        attrs.put("user_phone", user.getExtras().getString("phone"));
        attrs.put("company_id", Long.valueOf(group.getCompanyId()));
        attrs.put("department_id", Long.valueOf(group.getDepartmentId()));
        ServiceAccountRoleDepartmentMiddle pojo
                = ServiceAccountRoleDepartmentMiddle.dao.findById(group.getServiceAccountRoleDepartmentId());
        attrs.put("service_account_id", pojo.getLong("service_account_id"));
        Conversation.dao._setAttrs(attrs).save();
        // 添加 sw_group_conversation_middle 数据
        GroupConversationMiddle.dao
                .set("id",SnowflakeIdUtils.getInstance().nextId())
                .set("group_id",group.getGroup_id())
                .set("conversation_id",conversationId)
                .save();
    }

    /**
     * 构建查询分组sql
     * @param user
     * @return {@link {@link String}}
     * @author Darren
     * @date 2020/2/4 20:45
     */
    private String createSelectGroupSql(User user){
        StringBuilder sql = new StringBuilder();
        sql.append(" select ")
                .append(GROUP_PROPERTY)
                .append(" from sw_group where 1 = 1 ")
                .append(" and company_id = " + user.getExtras().getString("companyId"))
                .append(" and department_id = " + user.getExtras().getString("departmentId"))
                .append(
                        " and service_account_role_department_middle_id = "
                                + user.getExtras().getString("serviceAccountRoleDepartmentId")
                )
                .append(" and consumer_id = " + user.getId())
                .append(" order by create_time desc");
        return sql.toString();
    }

    /**
     * 获取容器中 接待数量最少的 客服
     * @param serviceAccountContainer
     * @return {@link {@link Map.Entry< String, String>}}
     * @author Darren
     * @date 2020/2/5 10:34
     */
    private Map.Entry<String,String> getServiceAccountMinReceptionNum( Map<String, String> serviceAccountContainer ){
        if(!CollectionUtils.isEmpty(serviceAccountContainer)){
            List<Map.Entry<String, String>> entries = serviceAccountContainer.entrySet().stream().collect(Collectors.toList());
            entries.sort( (o1,o2) -> (Integer.valueOf(o1.getValue()) - Integer.valueOf(o2.getValue())) );
            return entries.get(0);
        }
        return null;
    }

    /**
     * 把 Group 转成 Map 用于 jfianl框架的 添加使用
     * @param group
     * @return {@link {@link Map< String, Object>}}
     * @author Darren
     * @date 2020/2/4 20:30
     */
    private Map<String, Object> groupToMap(Group group){
        Map<String, Object> attrs = new HashMap<>();
        attrs.put("group_id", group.getGroup_id());
        attrs.put("name", group.getName());
        attrs.put("avatar", group.getAvatar());
        attrs.put("service_account_role_department_middle_id", group.getServiceAccountRoleDepartmentId());
        attrs.put("consumer_id", group.getConsumerId());
        attrs.put("group_type", group.getGroupType());
        attrs.put("company_id", group.getCompanyId());
        attrs.put("department_id", group.getDepartmentId());
        return attrs;
    }

    /**
     * 创建群组
     * @param user
     * @return {@link {@link Group}}
     * @author Darren
     * @date 2020/2/4 20:28
     */
    private Group createGroup(User user){
        //创建新的群组
        Group group = new Group();
        group.setGroup_id(String.valueOf(SnowflakeIdUtils.getInstance().nextId()));
        //群组名称 采用客户名称
        group.setName(user.getNick());
        group.setAvatar(user.getAvatar());
        group.setServiceAccountRoleDepartmentId(user.getExtras().getString("serviceAccountRoleDepartmentId"));
        group.setConsumerId(user.getId());
        //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
        if(VisitTypeEnum.CUSTOMER.getKey().equals(user.getVisitType())){
            group.setGroupType(GroupTypeEnum.CUSTOMER.getKey());
        }
        if(VisitTypeEnum.VISITOR.getKey().equals(user.getVisitType())){
            group.setGroupType(GroupTypeEnum.VISITOR.getKey());
        }
        group.setCompanyId(user.getExtras().getString("companyId"));
        group.setDepartmentId(user.getExtras().getString("departmentId"));
        return group;
    }

    /** ============================================= <客户> 处理 end================================================== */

    /** ============================================= 客服处理 start================================================== */

    /**
     * 创建 客服 user 数据
     * @param map
     * @return {@link {@link User}}
     * @author Darren
     * @date 2020/2/3 19:59
     */
    private User createServiceAccountUser(Map<String, String> map){
        User user = new User();
        //当前访问人为客服
        user.setId(map.get("serviceAccountRoleDepartmentId"));
        //用户头像
        String headImg = map.get("headImg") == null ? null : map.get("headImg").replace("\"", "");
        user.setAvatar(headImg);
        //user nick
        String nickname = map.get("nickname") == null ? null : map.get("nickname").replace("\"", "");
        user.setNick(nickname);
        //扩展预留字段;
        Map<String, Object> serviceAccountMap = new HashMap<>();
        serviceAccountMap.put("companyId", map.get("companyId"));
        serviceAccountMap.put("isDefault", map.get("isDefault"));
        serviceAccountMap.put("departmentId", map.get("departmentId"));
        serviceAccountMap.put("hasViewAllServiceAccount", map.get("hasViewAllServiceAccount"));
        serviceAccountMap.put("serviceAccountId", map.get("id"));
        serviceAccountMap.put("intoChat", map.get("intoChat"));
        if(Boolean.valueOf(map.get("intoChat"))){
            String type = map.get("type").replace("\"","");
            if(VisitTypeEnum.CUSTOMER.getKey().equals(type)){
                serviceAccountMap.put("customerDepartmentId", map.get("customerDepartmentId"));
                String customerNickname = map.get("customerNickname") == null ? null : map.get("customerNickname").replace("\"", "");
                serviceAccountMap.put("customerNickname", customerNickname);
                String customerHeadImg = map.get("customerHeadImg") == null ? null : map.get("customerHeadImg").replace("\"", "");
                serviceAccountMap.put("customerHeadImg", customerHeadImg);
                String customerPhone = map.get("customerPhone") == null ? null : map.get("customerPhone").replace("\"", "");
                serviceAccountMap.put("customerPhone", customerPhone);
                serviceAccountMap.put("customerId", map.get("customerId"));
                serviceAccountMap.put("type", type);
            }
            if(VisitTypeEnum.VISITOR.getKey().equals(type)){
                serviceAccountMap.put("visitorDepartmentId", map.get("visitorDepartmentId"));
                String visitorNickname = map.get("visitorNickname") == null ? null : map.get("visitorNickname").replace("\"", "");
                serviceAccountMap.put("visitorNickname", visitorNickname);
                String visitorHeadImg = map.get("visitorHeadImg") == null ? null : map.get("visitorHeadImg").replace("\"", "");
                serviceAccountMap.put("visitorHeadImg", visitorHeadImg);
                String visitorPhone = map.get("visitorPhone") == null ? null : map.get("visitorPhone").replace("\"", "");
                serviceAccountMap.put("visitorPhone", visitorPhone);
                serviceAccountMap.put("visitorId", map.get("visitorId"));
                String groupId = map.get("groupId") == null ? null : map.get("groupId").replace("\"", "");
                serviceAccountMap.put("groupId", groupId);
                serviceAccountMap.put("type", type);
            }
        }
        user.setExtras(new JSONObject(serviceAccountMap));
        //访问类型 (SERVICEACCOUNT 客服 / CUSTOMER 客户 / VISITOR 游客)
        user.setVisitType(VisitTypeEnum.SERVICEACCOUNT.getKey());
        user.setGroups(this.initServiceAccountGroups(user));
        user.setViewAllGroups(this.initViewAllServiceAccountGroup(user));
        user.setIntoChatGroups(this.initIntoChatGroups(user));
        return user;
    }

    /**
     * 初始化 由客服主动发起聊天的群组
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/17 9:43
     */
    private List<Group> initIntoChatGroups(User user) {
        List<Group> groups = new ArrayList<>();
        //是否创建 会话
        user.getExtras().put("isCreateConversation",true);
        String type = user.getExtras().getString("type");
        if( user.getExtras().getBoolean("intoChat") ){
            if(VisitTypeEnum.CUSTOMER.getKey().equals(type)){
                groups.add(this.disposeCustomerIntoChatGroupData(user));
            }else{
                groups.add(this.disposeVisitorIntoChatGroupData(user));
            }
            if( user.getExtras().getBoolean("isCreateConversation")){
                this.createIntoChatConversation(groups.get(0),user);
            }
        }
        return groups;
    }

    /**
     * 处理客服 向游客 发起聊天
     * @param user
     * @return {@link {@link Group}}
     * @author Darren
     * @date 2020/2/18 15:04
     */
    private Group disposeVisitorIntoChatGroupData(User user){
        // 群组 是留言群组 或 群组为在线状态 则不重新创建会话
        user.getExtras().put("isCreateConversation",false);
        ChatGroup chatGroup = ChatGroup.dao.findById(user.getExtras().get("groupId"));
        chatGroup
                // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
                .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                .update();
        return disposeChatGroupToGroup(chatGroup);
    }

    /**
     * 处理客服 向客户 发起聊天
     * @param user
     * @return {@link {@link Group}}
     * @author Darren
     * @date 2020/2/18 15:04
     */
    private Group disposeCustomerIntoChatGroupData(User user) {
        String customerDepartmentId = user.getExtras().getString("customerDepartmentId");
        StringBuilder sql = new StringBuilder();
        sql.append(" select ")
                .append(GROUP_PROPERTY)
                .append(" from sw_group where group_type = 1 ")
                .append(" and company_id = " + user.getExtras().getString("companyId"))
                .append(" and department_id = " + user.getExtras().getString("departmentId"))
                .append(
                        " and service_account_role_department_middle_id = " + user.getId()
                )
                .append(" and consumer_id = " + customerDepartmentId )
                .append(" order by create_time desc");
        ChatGroup chatGroup = ChatGroup.dao.findFirst(sql.toString());
        //customer 是否在线 如果不在线就是 留言群组
        boolean isOnline = isOnline(customerDepartmentId);
        if(null != chatGroup){
            //说明 当前存在 群组
            if(
                    MessageStateEnum.YES.getKey() == chatGroup.getInt("message_state") ||
                            GroupStateEnum.ON_LINE.getKey() == chatGroup.getInt("group_state")
            ){
                // 群组 是留言群组 或 群组为在线状态 则不重新创建会话
                user.getExtras().put("isCreateConversation",false);
            }
            chatGroup
                    // 更改 sw_group 表中 service_account_type 字段为 在线 (客服离线状态 1: 离线 0: 在线)
                    .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                    .update();
            if(isOnline){
                chatGroup
                        // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
                        .set("group_state",GroupStateEnum.ON_LINE.getKey())
                        // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
                        .set("consumer_type",ConsumerTypeEnum.ON_LINE.getKey())
                        // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
                        .set("message_state",MessageStateEnum.NO.getKey())
                        .update();
            }else{
                chatGroup
                        // 更改 sw_group 表中 group_state 字段为在线 (群组离线状态 1: 离线 0: 在线)
                        .set("group_state",GroupStateEnum.OFF_LINE.getKey())
                        // 更改 sw_group 表中 consumer_type 字段为在线 (客户/游客 离线状态 1:离线 0:在线)
                        .set("consumer_type",ConsumerTypeEnum.OFF_LINE.getKey())
                        // 更改 sw_group 表中 message_state 字段为 非留言 (群组留言状态 1:留言 0:非留言)
                        .set("message_state",MessageStateEnum.YES.getKey())
                        .update();
            }
            return disposeChatGroupToGroup(chatGroup);
        }else{
            //创建新的群组
            Group group = this.createIntoChatGroup(user);
            Map<String, Object> groupAttrs = this.groupToMap(group);
            groupAttrs.put("service_account_type", ServiceAccountOfflineTypeEnum.ON_LINE.getKey());
            if(isOnline){
                groupAttrs.put("group_state", GroupStateEnum.ON_LINE.getKey());
                groupAttrs.put("consumer_type", ConsumerTypeEnum.ON_LINE.getKey());
                groupAttrs.put("message_state", MessageStateEnum.NO.getKey());
            }else{
                groupAttrs.put("group_state", GroupStateEnum.OFF_LINE.getKey());
                groupAttrs.put("consumer_type", ConsumerTypeEnum.OFF_LINE.getKey());
                groupAttrs.put("message_state", MessageStateEnum.YES.getKey());
            }
            //jfianl框架的 保存
            ChatGroup.dao._setAttrs(groupAttrs).save();
            return group;
        }
    }

    /**
     * 创建客服邀请客户进入聊天的会话
     * @param group
     * @param user
     * @return {@link }
     * @author Darren
     * @date 2020/2/17 14:55
     */
    private void createIntoChatConversation(Group group, User user){
        Map<String, Object> attrs = new HashMap<>();
        Long conversationId = SnowflakeIdUtils.getInstance().nextId();
        attrs.put("id", conversationId );
        //会话状态 0：游客会话 1：客户会话  群组类型 0:游客-客服 类型 1:客户-客服 类型
        attrs.put("state", group.getGroupType());
        attrs.put("user_id", user.getExtras().getString("customerId"));
        attrs.put("user_nickname", user.getExtras().getString("customerNickname"));
        attrs.put("user_phone", user.getExtras().getString("customerPhone"));
        attrs.put("company_id", Long.valueOf(group.getCompanyId()));
        attrs.put("department_id", Long.valueOf(group.getDepartmentId()));
        ServiceAccountRoleDepartmentMiddle pojo
                = ServiceAccountRoleDepartmentMiddle.dao.findById(group.getServiceAccountRoleDepartmentId());
        attrs.put("service_account_id", pojo.getLong("service_account_id"));
        Conversation.dao._setAttrs(attrs).save();
        // 添加 sw_group_conversation_middle 数据
        GroupConversationMiddle.dao
                .set("id",SnowflakeIdUtils.getInstance().nextId())
                .set("group_id",group.getGroup_id())
                .set("conversation_id",conversationId)
                .save();
    }

    /**
     * 创建客服邀请客户 加入聊天群组
     * @param user
     * @return {@link {@link Group}}
     * @author Darren
     * @date 2020/2/17 10:43
     */
    private Group createIntoChatGroup(User user){
        Group group = new Group();
        group.setGroup_id(String.valueOf(SnowflakeIdUtils.getInstance().nextId()));
        //群组名称 采用客户名称
        group.setName(user.getExtras().getString("customerNickname"));
        group.setAvatar(user.getExtras().getString("customerHeadImg"));
        group.setServiceAccountRoleDepartmentId(user.getId());
        group.setConsumerId(user.getExtras().getString("customerDepartmentId"));
        group.setGroupType(GroupTypeEnum.CUSTOMER.getKey());
        group.setCompanyId(user.getExtras().getString("companyId"));
        group.setDepartmentId(user.getExtras().getString("departmentId"));
        return group;
    }

    /**
     * 判断用户是否在线
     * @param customerDepartmentId
     * @return {@link {@link boolean}}
     * @author Darren
     * @date 2020/2/17 10:03
     */
    private boolean isOnline(String customerDepartmentId){
        if(ImConst.ON.equals(imConfig.getIsStore())){
            MessageHelper messageHelper = imConfig.getMessageHelper();
            return messageHelper.isOnline(customerDepartmentId);
        }else{
            SetWithLock<ChannelContext> channelContexts = ImAio.getChannelContextsByUserId(customerDepartmentId);
            return (channelContexts != null && channelContexts.size() > 0);
        }
    }

    /**
     * 初始化 查看全部客服权限 才能看的群组数据
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/3 20:29
     */
    private List<Group> initViewAllServiceAccountGroup(User user) {
        List<Group> groups = new ArrayList<>();
        //1.判断当前客服是否具有查看当前部门下 全部客服通话的权限
        if( Boolean.valueOf(user.getExtras().getString("hasViewAllServiceAccount")) ){
            boolean flag = false;
            //获取 具有查看全部客服通话权限 的客服容器  并且 加入到该容器中   key数据格式: companyId : departmentId : ADMIN
            String adminContainerKey = user.getExtras().getString("companyId")
                    + ImConst.SEPARATOR + user.getExtras().getString("departmentId")
                    + ImConst.SEPARATOR +  ImConst.ADMIN ;
            List<String> adminContainer = jedisTemplate.listGetAll(adminContainerKey);
            if( CollectionUtils.isEmpty(adminContainer) ){
                //adminContainer 不存在
                jedisTemplate.listPushTail(adminContainerKey,user.getId());
                flag = true;
            }else{
                if( !adminContainer.contains(user.getId()) ) {
                    //如果容器中 不 包含这个 id 则添加
                    jedisTemplate.listPushTail(adminContainerKey,user.getId());
                    flag = true;
                }
            }

            //... 然后把当前 客服  加入到 该平台下 部门下 所有群组中
            if(flag){
                //从数据库中获取数据
                //1.查询所有留言群组
                StringBuilder sql1 = new StringBuilder();
                sql1
                        .append(" select ")
                        .append(GROUP_PROPERTY)
                        //message_state: 群组留言状态 1:留言 0:非留言
                        //group_type: 群组类型 0:游客-客服 类型 1:客户-客服 类型
                        .append(" from sw_group where group_type = 1 and message_state = 1 ")
                        .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
                        .append(" and department_id = " + user.getExtras().getString("departmentId") + " ");
                List<ChatGroup> messageChatGroups = ChatGroup.dao.find(sql1.toString());
                if( !CollectionUtils.isEmpty(messageChatGroups) ){
                    messageChatGroups = messageChatGroups.stream()
                            .filter(
                                    x -> !x.get("service_account_role_department_middle_id").equals(user.getId())
                            )
                            .collect(Collectors.toList());
                    List<Group> messageGroups = this.disposeChatGroupsToGroups(messageChatGroups);
                    if( !CollectionUtils.isEmpty(messageGroups) ){
                        groups.addAll(messageGroups);
                    }
                }

                //2.查询所有在线群组
                StringBuilder sql2 = new StringBuilder();
                sql2
                        .append(" select ")
                        .append(GROUP_PROPERTY)
                        //查看当前平台下 部门下 的所有在线群组
                        //group_state: 群组离线状态 1: 离线 0: 在线
                        //consumer_type: 客户/游客 离线状态 1:离线 0:在线
                        //message_state: 群组留言状态 1:留言 0:非留言
                        .append(" from sw_group where group_state = 0 and consumer_type = 0 and message_state = 0 ")
                        .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
                        .append(" and department_id = " + user.getExtras().getString("departmentId") + " ");
                List<ChatGroup> onlineChatGroups = ChatGroup.dao.find(sql2.toString());
                if( !CollectionUtils.isEmpty(onlineChatGroups) ){
                    onlineChatGroups = onlineChatGroups.stream()
                            .filter(
                                    x -> !x.get("service_account_role_department_middle_id").equals(user.getId())
                            )
                            .collect(Collectors.toList());
                    List<Group> onlineGroups = this.disposeChatGroupsToGroups(onlineChatGroups);
                    if( !CollectionUtils.isEmpty(onlineGroups) ){
                        groups.addAll(onlineGroups);
                    }
                }

            }

        }
        return groups;
    }

    /***
     * 初始化 客服对应的 群组数据
     * @param user
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/3 19:55
     */
    private List<Group> initServiceAccountGroups(User user) {

        List<ChatGroup> chatGroups = new ArrayList<>();
        //从数据库中获取数据
        //2.查询 客户/游客 对应平台下 对应部门下 在线群组状态 和 客户/游客在线状态 和 群组非留言状态
        StringBuilder sql2 = new StringBuilder();
        sql2
                .append(" select ")
                .append(GROUP_PROPERTY)
                //message_state: 群组留言状态 1:留言 0:非留言
                //consumer_type: 客户/游客 离线状态 1:离线 0:在线
                //group_state: 群组离线状态 1: 离线 0: 在线
                .append(" from sw_group where group_state = 0 and consumer_type = 0 and  message_state = 0")
                .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
                .append(" and department_id = " + user.getExtras().getString("departmentId") + " ")
                .append(
                        " and service_account_role_department_middle_id = " + user.getId()
                );
        List<ChatGroup> onlineChatGroups = ChatGroup.dao.find(sql2.toString());
        if( !CollectionUtils.isEmpty(onlineChatGroups) ){
            chatGroups.addAll(onlineChatGroups);
            //修改客服离线状态 为 在线
            for (ChatGroup chatGroup : onlineChatGroups) {
                chatGroup.set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                        .update();
            }
        }

        //1.查询 对应平台下 对应部门下 留言群组
        StringBuilder sql1 = new StringBuilder();
        sql1
                .append(" select ")
                .append(GROUP_PROPERTY)
                //message_state: 群组留言状态 1:留言 0:非留言
                //group_type: 群组类型 0:游客-客服 类型 1:客户-客服 类型
                .append(" from sw_group where group_type = 1 and message_state = 1 ")
                .append(" and company_id = "+ user.getExtras().getString("companyId") + " ")
                .append(" and department_id = " + user.getExtras().getString("departmentId") + " ")
                .append(
                        " and service_account_role_department_middle_id = " + user.getId()
                );
        List<ChatGroup> messageChatGroups = ChatGroup.dao.find(sql1.toString());
        if( !CollectionUtils.isEmpty(messageChatGroups) ){
            chatGroups.addAll(messageChatGroups);
            //修改 sw_group 表中的 message_state 状态;  群组留言状态 1:留言 0:非留言
            //修改 sw_group 表中的 service_account_type 状态; 客服离线状态 1: 离线 0: 在线
            for (ChatGroup chatGroup : messageChatGroups) {
                chatGroup .set("message_state", MessageStateEnum.NO.getKey())
                        .set("service_account_type",ServiceAccountOfflineTypeEnum.ON_LINE.getKey())
                        .update();
            }
        }

        return this.disposeChatGroupsToGroups(chatGroups);
    }

    /***
     * 处理 访问类型 为客服 的数据
     * @param
     * @return {@link }
     * @author Darren
     * @date 2020/2/3 14:01
     */
    private void disposeServiceAccountData(User user){
        //1.向客服容器中 添加客服 客服容器中过滤掉超级管理员
        // key组成 :  companyId : departmentId : SERVICEACCOUNT
        // value组成 : Map<String ( serviceAccountRoleDepartmentId ), String (当前连接客户人数)>
        String serviceAccountContainerKey
                =  user.getExtras().getString("companyId") + ImConst.SEPARATOR
                + user.getExtras().getString("departmentId") + ImConst.SEPARATOR
                + VisitTypeEnum.SERVICEACCOUNT.getKey() ;
        //判断该客服是否是超级管理员
        String isDefault = user.getExtras().getString("isDefault");
        if(DefaultStateEnum.NO.getKey().equals(isDefault)){
            List<Group> groups = user.getGroups();
            int num = 0;
            if(!CollectionUtils.isEmpty(groups)){
                num = groups.size();
            }
            List<Group> intoChatGroups = user.getIntoChatGroups();
            if(!CollectionUtils.isEmpty(groups)){
                num += intoChatGroups.size();
            }
            Map<String, String> serviceAccountContainer = jedisTemplate.hashGetAll(serviceAccountContainerKey);
            //判断 客服容器 是否存在
            if( CollectionUtils.isEmpty(serviceAccountContainer) ){
                //当前平台下 部门下  客服容器为null
                serviceAccountContainer = new HashMap<>();
                serviceAccountContainer.put(user.getId(),String.valueOf(num));
                jedisTemplate.hashMultipleSet(serviceAccountContainerKey,serviceAccountContainer);
            }else{
                //客服容器 存在
                //判断容器中是否存在这个客服
                if(serviceAccountContainer.containsKey(user.getId())){
                    //获取 当前客服 服务的 客户/游客 数量
                    Integer userNum = Integer.valueOf(serviceAccountContainer.get(user.getId())) + num;
                    jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf( userNum ));
                }else{
                    //不存在这个客服数据
                    jedisTemplate.hashSet(serviceAccountContainerKey,user.getId(),String.valueOf(num));
                }
            }
        }

    }

    /** ============================================= 客服处理 end =================================================== */

    /**
     * 转换数据 List<ChatGroup>  >>  List<Group>
     * @param chatGroups
     * @return {@link {@link List< Group>}}
     * @author Darren
     * @date 2020/2/4 14:27
     */
    private List<Group> disposeChatGroupsToGroups(List<ChatGroup> chatGroups){
        if( !CollectionUtils.isEmpty(chatGroups) ){
            List<Group> groups = new ArrayList<>();
            for (ChatGroup chatGroup : chatGroups) {
                Group group = this.disposeChatGroupToGroup(chatGroup);
                groups.add(group);
            }
            return groups;
        }
        return null;
    }

    /**
     * 转换数据 ChatGroup  >>  Group
     * @param chatGroup
     * @author Darren
     * @date 2020/2/4 14:27
     */
    private Group disposeChatGroupToGroup(ChatGroup chatGroup){
        if( null != chatGroup ){
            Group group = new Group();
            group.setGroup_id(chatGroup.getStr("group_id"));
            group.setName(chatGroup.getStr("name"));
            group.setAvatar(chatGroup.getStr("avatar"));
            group.setServiceAccountRoleDepartmentId(chatGroup.getStr("service_account_role_department_middle_id"));
            group.setConsumerId(chatGroup.getStr("consumer_id"));
            group.setGroupType(chatGroup.getInt("group_type"));
            group.setCompanyId(chatGroup.getStr("company_id"));
            group.setDepartmentId(chatGroup.getStr("department_id"));
            return group;
        }
        return null;
    }

    @Override
    public void onSuccess(ChannelContext channelContext) {
        logger.info("登录成功回调方法...");
    }

    @Override
    public boolean isProtocol(ChannelContext channelContext) {
        return true;
    }

    /**
     * 处理器的名称
     * @return
     */
    @Override
    public String name() {
        return ImConst.IM_CHAT_LOGIN_SERVICE_PROCESSOR;
    }
}
