package com.blueland.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.blueland.bo.*;
import com.blueland.constant.RedisConstant;
import com.blueland.core.util.RedisUtils;
import com.blueland.core.util.UserUtils;
import com.blueland.dao.*;
import com.blueland.model.TUcOrg;
import com.blueland.model.TUcStaff;
import com.blueland.service.ITucStaffService;
import com.blueland.service.SysRedisService;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

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

/**
 * @auther xcy
 * @create 2022-06-14 9:16
 */
@Service
public class SysRedisServiceImpl implements SysRedisService {

    private static final Logger log = LoggerFactory.getLogger(SysRedisServiceImpl.class);

    @Autowired
    private DictDao dictDao;

    @Autowired
    private Role2userDao role2userDao;

    @Autowired
    private MenuDao menuDao;

    @Autowired
    private TucStaffDao tucStaffDao;

    @Autowired
    private TucOrgDao tucOrgDao;

    @Autowired
    RedisUtils redisUtils;

    /**
     * 初始化字典数据
     */
    @Override
    public void initDict() {
        try {
            // 查询所有字典数据，不区分租户
            List<DictEntity> list = dictDao.getList("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    // 删除所有key
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_DICT);
                    List<DictEntity> cacheList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());
                    if (ObjectUtil.isNotEmpty(cacheList)) {
                        Map<String, DictEntity> dictMap = cacheList
                                .stream()
                                .collect(Collectors.toMap(el -> el.getDictParentId() + ":" + el.getDictCode(), e -> e));
                        redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_DICT, dictMap);
                    }
                }
            }
        } catch (Exception e) {
            log.error("字典初始化失败：" + e.getMessage());
        }
    }

    /**
     * 更新对应租户的字典数据
     */
    @Override
    public void flushDict() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<DictEntity> list = dictDao.getList(appId);
            if (CollUtil.isNotEmpty(list)) {
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_DICT);
                Map<String, DictEntity> dictMap = list
                        .stream()
                        .collect(Collectors.toMap(el -> el.getDictParentId() + ":" + el.getDictCode(), e -> e));
                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_DICT, dictMap);
            }
        } catch (Exception e) {
            log.error("字典刷新失败：" + e.getMessage());
        }
    }

    @Override
    public void saveOrUpdateDict(DictEntity model) {
        try {
            String appId = UserUtils.getTenantId();
            String item = model.getDictParentId() + ":" + model.getDictCode();
            if ("Y".equals(model.getStatus())) {
                redisUtils.hset(appId + ":" + RedisConstant.SYS_DICT, item, model);
            } else {
                redisUtils.hdel(appId + ":" + RedisConstant.SYS_DICT, item);
            }
        } catch (Exception e) {
            log.error("字典刷新失败：" + e.getMessage());
        }
    }

    @Override
    public List<DictEntity> getDictListByCode(String code) {
        String appId = UserUtils.getTenantId();
        List<DictEntity> list = new ArrayList<>();
        ScanOptions options =  new ScanOptions.ScanOptionsBuilder()
                .match(RedisConstant.WILDCARD + code)
                .count(100)
                .build();
        Cursor<Map.Entry<String, DictEntity>> cursor = redisUtils.hashScan(appId + ":" + RedisConstant.SYS_DICT, options);
        Map.Entry<String, DictEntity> entry = null;
        while (cursor.hasNext()) {
            entry = cursor.next();
            list.add(entry.getValue());
        }
        cursor.close();
        return list;
    }

    @Override
    public List<DictEntity> getDictListByParentCode(String parentCode) {
        String appId = UserUtils.getTenantId();
        List<DictEntity> list = new ArrayList<>();
        ScanOptions options =  new ScanOptions.ScanOptionsBuilder()
                .match(parentCode + RedisConstant.DIVISION + RedisConstant.WILDCARD)
                .count(100)
                .build();
        Cursor<Map.Entry<String, DictEntity>> cursor = redisUtils.hashScan(appId + ":" + RedisConstant.SYS_DICT, options);
        Map.Entry<String, DictEntity> entry = null;
        while (cursor.hasNext()) {
            entry = cursor.next();
            list.add(entry.getValue());
        }
        cursor.close();
        return list;
    }

    @Override
    public List<DictEntity> getDictList() {
        String appId = UserUtils.getTenantId();
        List<DictEntity> list = redisUtils.hValues(appId + ":" + RedisConstant.SYS_DICT, DictEntity.class);
        return list;
    }

    public DictEntity getDict(String code, String parentCode) {
        String appId = UserUtils.getTenantId();
        DictEntity dict = redisUtils.hget(appId + ":" + RedisConstant.SYS_DICT, parentCode + ":" + code, DictEntity.class);
        return dict;
    }

    @Override
    public DictEntity getDictByRedisKey(String item) {
        String appId = UserUtils.getTenantId();
        String key = appId + ":" + RedisConstant.SYS_DICT;
        DictEntity dict = redisUtils.hget(key, item, DictEntity.class);
        return dict;
    }

    /**
     * 加载租户id:员工编码 对应 List<租户id:角色id>的redis数据
     */
    @Override
    public void initRole() {
        try {
            List<AppIdUserIdAppIdRoleIdDTO> list = role2userDao.findListRoleId("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    // 删除所有key
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_ROLE);
                    List<AppIdUserIdAppIdRoleIdDTO> roleList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());

                    Map<String, List<String>> roleMap = roleList.stream()
                            .collect(Collectors.toMap(e1 -> Joiner.on(":").join(Lists.newArrayList(e1.getAppId(), e1.getUserId())), e2 -> e2.getList()));
                    redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_ROLE, roleMap);
                }
            }
        } catch (Exception e) {
            log.error("角色加载失败：" + e.getMessage());
        }
    }

    @Override
    public void flushRole() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<AppIdUserIdAppIdRoleIdDTO> list = role2userDao.findListRoleId(appId);
            if (ObjectUtil.isNotEmpty(list)) {
                // 删除所有key
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_ROLE);
                //重新缓存新的数据
                Map<String, List<String>> roleMap = list.stream()
                        .collect(Collectors.toMap(e1 -> Joiner.on(":").join(Lists.newArrayList(e1.getAppId(), e1.getUserId())), e2 -> e2.getList()));
                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_ROLE, roleMap);
            }
        } catch (Exception e) {
            log.error("角色刷新失败：" + e.getMessage());
        }
    }

    @Override
    public void removeRoleByKey(String userId) {
        try {
            String appId = UserUtils.getTenantId();
            redisUtils.hdel(appId + ":" + RedisConstant.SYS_ROLE, appId + ":" + userId);
        } catch (Exception e) {
            log.error("删除角色失败：" + e.getMessage());
        }
    }

    @Override
    public void saveRoleCache(String userId, String roleId) {
        try {
            String appId = UserUtils.getTenantId();
            List<String> list = (List<String>) redisUtils.hget(appId + ":" + RedisConstant.SYS_ROLE, appId + ":" + userId);
            if (ObjectUtil.isEmpty(list)) {
                list = new ArrayList<>();
            }
            if (!list.contains(appId + ":" + roleId)) {
                list.add(appId + ":" + roleId);
            }
            redisUtils.hset(appId + ":" + RedisConstant.SYS_ROLE, appId + ":" + userId, list);
        } catch (Exception e) {
            log.error("保存角色失败：" + e.getMessage());
        }
    }

    /**
     * 加载租户id:菜单类型type:菜单id对应菜单实体的redis数据
     */
    @Override
    public void initMenu() {
        try {
            List<MenuEntity> list = menuDao.findMenuList("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    // 删除所有key
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_MENU);
                    List<MenuEntity> menuList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());
                    Map<String, MenuEntity> menuMap = menuList.stream()
                            .collect(Collectors.toMap(e1 -> Joiner.on(":").join(Lists.newArrayList(e1.getMenuType(), e1.getMenuId())), e2 -> e2));

                    redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_MENU, menuMap);
                }
            }
        } catch (Exception e) {
            log.error("加载菜单失败：" + e.getMessage());
        }
    }

    @Override
    public void flushMenu() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<MenuEntity> list = menuDao.findMenuList(appId);
            if (ObjectUtil.isNotEmpty(list)) {
                // 删除所有key
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_MENU);
                Map<String, MenuEntity> menuMap = list.stream()
                        .collect(Collectors.toMap(e1 -> Joiner.on(":").join(Lists.newArrayList(e1.getMenuType(), e1.getMenuId())), e2 -> e2));

                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_MENU, menuMap);
            }
        } catch (Exception e) {
            log.error("刷新菜单失败：" + e.getMessage());
        }
    }

    @Override
    public void saveOrUpdateMenu(MenuEntity model) {
        try {
            String appId = UserUtils.getTenantId();
            redisUtils.hset(appId + ":" + RedisConstant.SYS_MENU, model.getMenuType() + ":" + model.getMenuId(), model);
        } catch (Exception e) {
            log.error("新增菜单失败：" + e.getMessage());
        }
    }

    /**
     * 加载租户id:角色id对应List<租户id:菜单类型type:菜单id>的redis数据
     */
    @Override
    public void initMenuId() {
        try {
            List<RoleMenuRedisDTO> list = menuDao.findListRoleMenu("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    // 删除所有key
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_MENU_ID);
                    List<RoleMenuRedisDTO> roleMenuRedisList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());
                    Map<String, List<String>> dataMap = roleMenuRedisList.stream()
                            .collect(Collectors.toMap(e1 -> e1.getRoleId(), e2 -> e2.getList()));

                    redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_MENU_ID, dataMap);
                }
            }
        } catch (Exception e) {
            log.error("加载菜单id失败：" + e.getMessage());
        }
    }

    @Override
    public void flushMenuId() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<RoleMenuRedisDTO> list = menuDao.findListRoleMenu(appId);
            if (ObjectUtil.isNotEmpty(list)) {
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_MENU_ID);
                Map<String, List<String>> dataMap = list.stream()
                        .collect(Collectors.toMap(e1 -> e1.getRoleId(), e2 -> e2.getList()));

                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_MENU_ID, dataMap);
            }
        } catch (Exception e) {
            log.error("刷新菜单id失败：" + e.getMessage());
        }
    }

    @Override
    public void initStaff() {
        try {
            List<TucStaffEntity> list = tucStaffDao.findListQY("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_STAFF);
                    List<TucStaffEntity> staffList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());
                    Map<String, TucStaffEntity> dataMap = staffList.stream()
                            .collect(Collectors.toMap(e1 -> e1.getStaffPostCode(), e2 -> e2, (last, next) -> next));
                    redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_STAFF, dataMap);
                }
            }
        } catch (Exception e) {
            log.error("加载员工失败：" + e.getMessage());
        }
    }

    @Override
    public void flushStaff() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<TucStaffEntity> list = tucStaffDao.findListQY(appId);
            if (CollUtil.isNotEmpty(list)) {
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_STAFF);
                Map<String, TucStaffEntity> dataMap = list.stream()
                        .collect(Collectors.toMap(e1 -> e1.getStaffPostCode(), e2 -> e2));
                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_STAFF, dataMap);
            }
        } catch (Exception e) {
            log.error("刷新员工失败：" + e.getMessage());
        }
    }

    @Override
    public void flushStaff(List<TUcStaff> list) {
        try {
            if (ObjectUtil.isNotEmpty(list)) {
                for (TUcStaff staff : list) {
                    TucStaffEntity entity = BeanUtil.copyProperties(staff, TucStaffEntity.class);
                    if (Objects.equals("QY", entity.getStaffStatus())) {
                        redisUtils.hset(entity.getAppId() + ":" + RedisConstant.SYS_STAFF, entity.getStaffPostCode(), entity);
                    }
                }
            }
        } catch (Exception e) {
            log.error("刷新员工数据失败：" + e.getMessage());
        }
    }

    @Override
    public void saveOrUpdateStaff(TucStaffEntity model) {
        try {
            if ("QY".equals(model.getStaffStatus())) {
                redisUtils.hset(model.getAppId() + ":" + RedisConstant.SYS_STAFF, model.getStaffPostCode(), model);
            } else {
                redisUtils.hdel(model.getAppId() + ":" + RedisConstant.SYS_STAFF, model.getStaffPostCode());
            }
        } catch (Exception e) {
            log.error("新增员工失败：" + e.getMessage());
        }
    }

    @Override
    public void removeStaff(String appId, String staffPostCode) {
        try {
            redisUtils.hdel(appId + ":" + RedisConstant.SYS_STAFF, staffPostCode);
        } catch (Exception e) {
            log.error("移除员工失败：" + e.getMessage());
        }
    }

    @Override
    public List<TucStaffEntity> getStaffList() {
        String appId = UserUtils.getAppId();
        List<TucStaffEntity> list = redisUtils.hValues(appId + ":" + RedisConstant.SYS_STAFF, TucStaffEntity.class);
        return list;
    }

    @Override
    public TucStaffEntity getStaff(String staffPostCode) {
        String appId = UserUtils.getAppId();
        TucStaffEntity staff = redisUtils.hget(appId + ":" + RedisConstant.SYS_DICT, staffPostCode, TucStaffEntity.class);
        return staff;
    }

    @Override
    public List<TucStaffEntity> getStaffList(List<String> staffPostCodes) {
        String appId = UserUtils.getTenantId();
        List<TucStaffEntity> list =
                redisUtils.multiGet(appId + ":" + RedisConstant.SYS_STAFF, staffPostCodes, TucStaffEntity.class);
        return list;
    }

    @Override
    public void initDept() {
        try {
            List<Org> list = tucOrgDao.list("");
            if (CollUtil.isNotEmpty(list)) {
                // 获取所有租户编码集合
                List<String> appIds = list.stream().map(x -> x.getAppId()).
                        collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
                for (String appId : appIds) {
                    redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_DEPT);
                    List<Org> orgList = list.stream().filter(x -> x.getAppId().equals(appId)).collect(Collectors.toList());
                    Map<String, Org> dataMap = orgList.stream()
                            .collect(Collectors.toMap(e1 -> e1.getDeptId(), e2 -> e2, (next, last) -> next));
                    redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_DEPT, dataMap);
                }
            }
        } catch (Exception e) {
            log.error("加载部门失败：" + e.getMessage());
        }
    }

    @Override
    public void flushDept() {
        try {
            // 根据租户查询对应的数据进行更新
            String appId = UserUtils.getTenantId();
            List<Org> list = tucOrgDao.list(appId);
            if (CollUtil.isNotEmpty(list)) {
                redisUtils.deleteAll(appId + ":" + RedisConstant.SYS_DEPT);
                Map<String, Org> dataMap = list.stream()
                        .collect(Collectors.toMap(e1 -> e1.getDeptId(), e2 -> e2));
                redisUtils.hPutAll(appId + ":" + RedisConstant.SYS_DEPT, dataMap);
            }
        } catch (Exception e) {
            log.error("刷新部门失败：" + e.getMessage());
        }
    }

    @Override
    public void flushDept(List<TUcOrg> list)  {
        try {
            if (ObjectUtil.isNotEmpty(list)) {
                for (TUcOrg org : list) {
                    if ("QY".equals(org.getOrgStatus())) {
                        Org o = BeanUtil.copyProperties(org, Org.class);
                        redisUtils.hset(org.getAppId() + ":" + RedisConstant.SYS_DEPT, org.getDeptId(), o);
                    }
                }
            }
        } catch (Exception e) {
            log.error("刷新部门数据失败：" + e.getMessage());
        }
    }

    @Override
    public void saveOrUpdateDept(Org org) {
        try {
            if ("QY".equals(org.getOrgStatus())) {
                redisUtils.hset(org.getAppId() + ":" + RedisConstant.SYS_DEPT, org.getDeptId(), org);
            } else {
                redisUtils.hdel(org.getAppId() + ":" + RedisConstant.SYS_DEPT, org.getDeptId());
            }
        } catch (Exception e) {
            log.error("新增部门失败：" + e.getMessage());
        }
    }

    @Override
    public void removeDeptByKey(String appId, String deptId) {
        try {
            redisUtils.hdel(appId + ":" + RedisConstant.SYS_DEPT, deptId);
        } catch (Exception e) {
            log.error("移除部门失败：" + e.getMessage());
        }
    }

    @Override
    public Org getOrg(String deptId) {
        String appId = UserUtils.getTenantId();
        Org org = redisUtils.hget(appId + ":" + RedisConstant.SYS_DEPT, deptId, Org.class);
        return org;
    }

    @Override
    public List<Org> getOrgList() {
        String appId = UserUtils.getTenantId();
        List<Org> list = redisUtils.hValues(appId + ":" + RedisConstant.SYS_DEPT, Org.class);
        return list;
    }

    @Override
    public List<Org> getOrgList(List<String> deptIds) {
        String appId = UserUtils.getTenantId();
        List<Org> list =
                redisUtils.multiGet(appId + ":" + RedisConstant.SYS_DEPT, deptIds, Org.class);
        return list;
    }
}
