package com.hfkj.service.impl;

import com.hfkj.common.exception.ErrorCode;
import com.hfkj.common.exception.ErrorHelp;
import com.hfkj.common.exception.SysCode;
import com.hfkj.common.utils.RedisUtil;
import com.hfkj.common.utils.StreamUtil;
import com.hfkj.dao.SecMenuMapper;
import com.hfkj.dao.SecRoleMenuRelMapper;
import com.hfkj.entity.SecMenu;
import com.hfkj.entity.SecMenuExample;
import com.hfkj.entity.SecRole;
import com.hfkj.entity.SecRoleMenuRel;
import com.hfkj.model.MenuTreeModel;
import com.hfkj.service.SecMenuService;
import com.hfkj.service.SecRoleMenuRelService;
import com.hfkj.service.SecRoleService;
import com.hfkj.sysenum.SecMenuTypeEnum;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @className: SecMenuServiceImpl
 * @author: HuRui
 * @date: 2024/3/28
 **/
@Service("secMenuService")
public class SecMenuServiceImpl implements SecMenuService {

    @Resource
    private SecMenuMapper secMenuMapper;
    @Resource
    private SecRoleService secRoleService;
    @Resource
    private SecRoleMenuRelService secRoleMenuRelService;
    @Resource
    private RedisUtil redisUtil;
    // 缓存k
    private static final String CACHE_MENU = "SEC_MENU";
    private static final String CACHE_ROLE_MENU = "SEC_ROLE_MENU";

    @Override
    public void create(SecMenu menu) {
        menu.setCreateTime(new Date());
        menu.setUpdateTime(new Date());
        secMenuMapper.insert(menu);
        // 加入缓存
        redisUtil.hset(CACHE_MENU, ""+menu.getId(), menu);
    }

    @Override
    public void update(SecMenu menu) {
        menu.setCreateTime(new Date());
        menu.setUpdateTime(new Date());
        secMenuMapper.updateByPrimaryKey(menu);

        // 查询角色菜单
        List<SecRoleMenuRel> relList = secRoleMenuRelService.getRelListByMenu(menu.getId());
        for (SecRoleMenuRel rel : relList) {
            // 删除角色菜单缓存
            redisUtil.hdel(CACHE_ROLE_MENU, ""+rel.getRoleId());
        }

        // 更新菜单缓存
        redisUtil.hset(CACHE_MENU, ""+menu.getId(), menu);
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW,rollbackFor= {RuntimeException.class})
    public void delete(Long menuId) {
        // 查询菜单
        SecMenu secMenu = queryDetail(menuId);
        if (secMenu == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到数据");
        }
        secMenuMapper.deleteByPrimaryKey(menuId);

        // 查询角色菜单
        List<SecRoleMenuRel> relList = secRoleMenuRelService.getRelListByMenu(menuId);
        for (SecRoleMenuRel rel : relList) {
            secRoleMenuRelService.delete(rel.getId());
            // 删除角色菜单缓存
            redisUtil.hdel(CACHE_ROLE_MENU, ""+rel.getRoleId());
        }
        // 删除缓存
        redisUtil.hdel(CACHE_MENU, ""+secMenu.getId());
    }

    @Override
    public SecMenu queryDetail(Long menuId) {
        Object cacheObj = redisUtil.hget(CACHE_MENU, "" + menuId);
        if (cacheObj != null) {
            return (SecMenu) cacheObj;
        }
        SecMenu menu = secMenuMapper.selectByPrimaryKey(menuId);
        // 更新菜单缓存
        redisUtil.hset(CACHE_MENU, ""+menu.getId(), menu);
        return menu;
    }

    @Override
    public List<SecMenu> getList(Map<String, Object> param) {
        SecMenuExample example = new SecMenuExample();
        SecMenuExample.Criteria criteria = example.createCriteria();

        if (StringUtils.isNotBlank(MapUtils.getString(param, "menuName"))) {
            criteria.andMenuNameLike("%"+MapUtils.getString(param, "menuName")+"%");
        }

        if (MapUtils.getInteger(param, "menuType") != null) {
            criteria.andMenuTypeEqualTo(MapUtils.getInteger(param, "menuType"));
        }

        example.setOrderByClause("menu_sort desc");
        return secMenuMapper.selectByExample(example);
    }

    @Override
    public List<SecMenu> queryRoleMenu(Long roleId, SecMenuTypeEnum menuType) {
        // 获取角色菜单关系
        Map<Long, SecRoleMenuRel> roleMenuRelMap = secRoleMenuRelService.getRelListByRole(roleId).stream()
                .collect(Collectors.toMap(SecRoleMenuRel::getMenuId, Function.identity()));

        // 获取系统菜单
        List<SecMenu> menuList = getAllList().stream()
                .filter(o->o.getMenuType().equals(menuType.getCode()))
                .collect(Collectors.toList());

        Iterator<SecMenu> iterator = menuList.iterator();
        while (iterator.hasNext()) {
            if (roleMenuRelMap.get(iterator.next().getId()) == null) {
                iterator.remove();
            }
        }
        return menuList;
    }

    @Override
    public List<SecMenu> getAllList() {
        Map cacheObj = redisUtil.hmget(CACHE_MENU);
        if (!cacheObj.isEmpty()) {
            return new ArrayList<>(cacheObj.values());
        }
        SecMenuExample example = new SecMenuExample();
        example.createCriteria();
        List<SecMenu> menuList = secMenuMapper.selectByExample(example);
        for (SecMenu menu : menuList) {
            redisUtil.hset(CACHE_MENU, ""+menu.getId(), menu);
        }
        return menuList;
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW,rollbackFor= {RuntimeException.class})
    public void assignMenu(Long roleId, List<Long> menuIds) {
        // 查询角色
        SecRole secRole = secRoleService.getDetail(roleId);
        if (secRole == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的角色");
        }
        // 记录更新时间
        secRoleService.editData(secRole);

        // 删除角色权限旧数据
        secRoleMenuRelService.deleteByRole(roleId);

        SecRoleMenuRel rel;
        List<SecRoleMenuRel> relList = new ArrayList<>();
        for (Long menuId : menuIds) {
            rel = new SecRoleMenuRel();
            rel.setRoleId(roleId);
            rel.setMenuId(menuId);
            relList.add(rel);
        }
        // 根据菜单id去重
        List<SecRoleMenuRel> collectList = relList.stream().filter(StreamUtil.distinctByKey(b -> b.getMenuId())).collect(Collectors.toList());
        // 角色新菜单权限
        secRoleMenuRelService.batchAdd(collectList);
        // 更新缓存
        redisUtil.hdel(CACHE_ROLE_MENU, ""+roleId);
    }

    @Override
    public List<MenuTreeModel> queryMenuTree(Long roleId) {
        List<MenuTreeModel> treeModelList = new ArrayList<>();
        MenuTreeModel menuTree;

        if (roleId != null) {
            // 获取缓存
            Object cacheObj = redisUtil.hget(CACHE_ROLE_MENU, ""+roleId);
            if (cacheObj != null) {
                return (List<MenuTreeModel>) cacheObj;
            }
            // 获取缓存菜单
            Map<String,Object> secMenuMap = (Map) redisUtil.hmget(CACHE_MENU);
            if (secMenuMap.isEmpty()) {
                getAllList(); // 刷新缓存
                secMenuMap = (Map) redisUtil.hmget(CACHE_MENU);
            }
            // 角色菜单
            List<SecMenu> roleMenuList = new ArrayList<>();
            // 查询角色菜单关系
            List<SecRoleMenuRel> roleMenuRelList = secRoleMenuRelService.getRelListByRole(roleId);
            for (SecRoleMenuRel rel : roleMenuRelList) {
                Object obj = secMenuMap.get(""+rel.getMenuId());
                roleMenuList.add((SecMenu) obj);
            }
            // 获取最顶层菜单
            List<SecMenu> topLevelMenuList = roleMenuList.stream()
                    .filter(o -> o.getMenuPSid() == null)
                    .sorted(Comparator.comparing(SecMenu::getMenuSort))
                    .collect(Collectors.toList());
            for (SecMenu topLevelMenu : topLevelMenuList) {
                if (topLevelMenu.getMenuType().equals(SecMenuTypeEnum.type1.getCode())) {
                    menuTree = new MenuTreeModel();
                    menuTree.setId(topLevelMenu.getId());
                    menuTree.setMenuName(topLevelMenu.getMenuName());
                    menuTree.setMenuType(topLevelMenu.getMenuType());
                    menuTree.setMenuUrl(topLevelMenu.getMenuUrl());
                    menuTree.setMenuUrlImg(topLevelMenu.getMenuUrlImg());
                    menuTree.setMenuPSid(topLevelMenu.getMenuPSid());
                    menuTree.setMenuSort(topLevelMenu.getMenuSort());
                    menuTree.setMenuDesc(topLevelMenu.getMenuDesc());
                    // 获取下级菜单
                    menuTree.setChildMenuList(recursionMenu(roleMenuList, topLevelMenu.getId()));
                    treeModelList.add(menuTree);
                }
            }
            // 存入缓存
            redisUtil.hset(CACHE_ROLE_MENU, ""+roleId, treeModelList);
            return treeModelList;
        } else {
            List<SecMenu> menuList = getAllList();
            // 获取最顶层菜单
            List<SecMenu> topLevelMenuList = menuList.stream()
                    .filter(o -> o.getMenuPSid() == null)
                    .sorted(Comparator.comparing(SecMenu::getMenuSort))
                    .collect(Collectors.toList());
            for (SecMenu topLevelMenu : topLevelMenuList) {
                if (topLevelMenu.getMenuType().equals(SecMenuTypeEnum.type1.getCode())) {
                    menuTree = new MenuTreeModel();
                    menuTree.setId(topLevelMenu.getId());
                    menuTree.setMenuName(topLevelMenu.getMenuName());
                    menuTree.setMenuType(topLevelMenu.getMenuType());
                    menuTree.setMenuUrl(topLevelMenu.getMenuUrl());
                    menuTree.setMenuUrlImg(topLevelMenu.getMenuUrlImg());
                    menuTree.setMenuPSid(topLevelMenu.getMenuPSid());
                    menuTree.setMenuSort(topLevelMenu.getMenuSort());
                    menuTree.setMenuDesc(topLevelMenu.getMenuDesc());
                    // 获取下级菜单
                    menuTree.setChildMenuList(recursionMenu(menuList, topLevelMenu.getId()));
                    treeModelList.add(menuTree);
                }
            }
            return treeModelList;
        }
    }

    /**
     * 递归获取菜单
     * @param dataSource 数据源
     * @param parentMenuId 父级菜单id
     * @return
     */
    public List<MenuTreeModel> recursionMenu(List<SecMenu> dataSource, Long parentMenuId) {
        List<MenuTreeModel> treeModelList = new ArrayList<>();
        MenuTreeModel menuTree;

        List<SecMenu> collect = dataSource.stream()
                .filter(o -> o.getMenuPSid() != null && o.getMenuPSid().equals(parentMenuId))
                .sorted(Comparator.comparing(SecMenu::getMenuSort))
                .collect(Collectors.toList());
        for (SecMenu menu : collect) {
            if (menu.getMenuType().equals(SecMenuTypeEnum.type1.getCode())) {
                menuTree = new MenuTreeModel();
                menuTree.setId(menu.getId());
                menuTree.setMenuName(menu.getMenuName());
                menuTree.setMenuType(menu.getMenuType());
                menuTree.setMenuUrl(menu.getMenuUrl());
                menuTree.setMenuUrlImg(menu.getMenuUrlImg());
                menuTree.setMenuPSid(menu.getMenuPSid());
                menuTree.setMenuSort(menu.getMenuSort());
                menuTree.setMenuDesc(menu.getMenuDesc());
                menuTree.setChildMenuList(recursionMenu(dataSource, menu.getId()));
                treeModelList.add(menuTree);
            }
        }
        return treeModelList;
    }

}
