package com.sptg.permissions.biz.service.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.enums.SqlLike;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.sptg.common.core.beans.ResultBean;
import com.sptg.common.core.beans.ResultEnum;
import com.sptg.common.core.constant.OperationConstant;
import com.sptg.common.core.constant.SymbolConstant;
import com.sptg.common.core.util.AssertUtil;
import com.sptg.common.core.util.JsonUtils;
import com.sptg.permissions.api.dto.MenuDto;
import com.sptg.permissions.api.entity.Menu;
import com.sptg.permissions.api.entity.RoleMenu;
import com.sptg.permissions.api.enums.PermissionsEnum;
import com.sptg.permissions.biz.mapper.MenuMapper;
import com.sptg.permissions.biz.mapper.RoleMenuMapper;
import com.sptg.permissions.biz.service.MenuServiceInterface;
import com.sptg.permissions.biz.service.RoleMenuServiceInterface;

import lombok.RequiredArgsConstructor;

/**
 * title: MenuServiceImpl
 * package: com.sptg.permissions.biz.security.impl
 * description: 菜单-服务层接口实现类
 *
 *
 *
 */
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuServiceInterface {

    /**
     * 菜单缓存名称
     */
   private static final String MENU_CACHE_NAME = "sptg:permissions:menu";

    @Resource
    private RoleMenuMapper roleMenuMapper;
    private final RedisTemplate<String,String> redisTemplate;
    private final RoleMenuServiceInterface roleMenuServiceInterface;

    /**
     * 超级管理员的角色Id
     */
    private static final Long SUPER_ADMIN_ROLE_ID = 10001L;

    /* ------------------------------------- 重写父类查询方法，过滤已删除的数据 ------------------------------------- */
    /* -------------------------------------------------------------------------------------------------------------- */

    @Override
    public Menu selectOne(Wrapper<Menu> wrapper) {
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return super.selectOne(wrapper);
    }

    @Override
    public List<Menu> selectList(Wrapper<Menu> wrapper) {
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return super.selectList(wrapper);
    }

    /**
     * 功能描述: 重写父类方法,根据Id修改菜单,修改后删除缓存
     * @param entity 菜单信息
     * @return boolean { true：成功，false：失败 }
     *
     * date:  2018/11/4 23:11
     */
    @Override
    public boolean updateById(Menu entity) {
        boolean result = super.updateById(entity);
        this.redisTemplate.opsForHash().delete(MENU_CACHE_NAME,entity.getId().toString());
        return result;
    }

    /* -------------------------------------------------- 业务代码 -------------------------------------------------- */
    /* -------------------------------------------------------------------------------------------------------------- */

    /**
     * 功能描述:根据Id查询菜单，并将查询结果缓存到Redis中
     * @param id 主键
     * @return Menu
     *
     *
     */
    @Override
    public Menu selectById(Serializable id) {
        //  查询缓存
        HashOperations<String, String, String> hashOperations = this.redisTemplate.opsForHash();
        String hashKey = id.toString();
        String json = hashOperations.get(MENU_CACHE_NAME, hashKey);
        if (StringUtils.isEmpty(json)){
            //  删除数据库
            Wrapper<Menu> wrapper = new EntityWrapper<>();
            wrapper.eq("l_id",id);
            Menu menu = this.selectOne(wrapper);
            if (null != menu){
                //  加入缓存
                json = JsonUtils.objectToJson(menu);
                hashOperations.put(MENU_CACHE_NAME,hashKey,json);
            }
            return menu;
        }
        return JsonUtils.jsonToPojo(json,Menu.class);
    }

    @Override
    public List<Menu> selectListByRoleIdList(List<Long> roleIdList) {
        if (null == roleIdList || roleIdList.size() == 0){
            return new ArrayList<>(0);
        }
        Wrapper<RoleMenu> wrapper = new EntityWrapper<>();
        //  设置查询条件：角色Id In（roleIdList）
        wrapper.in("l_role_id",roleIdList);
        //  根据菜单Id分组
        /*wrapper.groupBy("l_menu_id");*/
        //  获取角色与菜单的关联列表
        List<RoleMenu> roleMenuList = this.roleMenuMapper.selectList(wrapper);
        //  获取菜单列表
        List<Menu> menuList = new ArrayList<>(roleMenuList.size());
        Set<Long> menuIds=new HashSet<Long>();
        roleMenuList.forEach(roleMenu -> {
        	if(!menuIds.contains(roleMenu.getMenuId())) {
        		 Menu menu=this.selectById(roleMenu.getMenuId());
                 if (null != menu){
                     menuList.add(menu);
                 }
                 menuIds.add(roleMenu.getMenuId());
        	}
        });
        return menuList;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ResultBean<Boolean> insertMenu(MenuDto entity) {
        return this.operationMenu(entity,menuDto -> {
            //  判断是否有父Id
            if (null == menuDto.getPid()){
                menuDto.setPid((long)OperationConstant.ONE);
            }
            //  获取父菜单
            Menu parentMenu = this.selectById(menuDto.getPid());
            //  父菜单子节点总数+1
            int count = parentMenu.getClientCount() + 1;
            parentMenu.setClientCount(count);
            int layerNumer = parentMenu.getLayerNumber() + 1;
            //  设置排序值，子节点总数，层级，数据状态
            menuDto.setSort(count);
            menuDto.setClientCount(OperationConstant.ZERO);
            menuDto.setLayerNumber(layerNumer);
            menuDto.setStatus(OperationConstant.ZERO);
            //  保存修改
            boolean result = false;
            if (this.updateById(parentMenu)){
                result = this.insert(menuDto);
                if (result) {
                    menuDto.setPosition(parentMenu.getPosition() + menuDto.getId() + SymbolConstant.BACK_SLASH);
                    this.updateById(menuDto);

                    //  创建授权对象
                    RoleMenu roleMenu = new RoleMenu();
                    //  菜单Id
                    roleMenu.setMenuId(menuDto.getId());
                    //  角色Id
                    roleMenu.setRoleId(SUPER_ADMIN_ROLE_ID);
                    //  创建人Id
                    roleMenu.setCreateAccountId(menuDto.getCreateAccountId());
                    //  保存授权信息
                    result = this.roleMenuServiceInterface.insert(roleMenu);
                }
            }
            return new ResultBean<>(ResultEnum.SUCCESS,result);
        });
    }

    /**
     * 功能描述: 校验Token是否已存在
     * @param token 权限标识
     * @return boolean：{ true：已存在，false：不存在 }
     *
     *
     */
    private boolean checkTokenIsExist(String token){
        //  构建查询条件
        Wrapper<Menu> wrapper = new EntityWrapper<>();
        //  设置查询条件
        wrapper.eq("s_token",token);
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return this.selectCount(wrapper) > 0;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ResultBean<Boolean> updateMenu(MenuDto entity) {
        //  获取修改前信息
        Menu dbMenu = this.selectById(entity.getId());
        Assert.notNull(dbMenu,"菜单的Id不存在");
        //  判断是否修改了Token
        if (dbMenu.getToken().equals(entity.getToken())){
            //  Token 没有发送修改，不进行校验
            entity.setCheckToken(false);
        }
        return this.operationMenu(entity,menuDto -> {
            //  判断是否修改了父节点
            if (!dbMenu.getPid().equals(menuDto.getPid())){
                //  获取新的父节点信息
                Menu parentMenu = this.selectById(menuDto.getPid());
                Assert.notNull(parentMenu,"父级菜单Id不存在");
                //  生成新的 postion
                String position = parentMenu.getPosition() + menuDto.getId() + SymbolConstant.BACK_SLASH;
                menuDto.setPosition(position);
                //  排序值
                int count = parentMenu.getClientCount() + 1;
                menuDto.setSort(count);
                //  新的层级数量
                int layerNumber = parentMenu.getLayerNumber() + 1;
                menuDto.setLayerNumber(layerNumber);
                //  修改父节点的子节点数量
                parentMenu.setClientCount(count);
                int layerCha = layerNumber - dbMenu.getLayerNumber();

                //  获取子节点列表
                List<Menu> subMenuList = this.selectSubMenuListByPosition(dbMenu.getPosition());
                subMenuList.forEach(subMenu -> {
                    //  替换position
                    String subPosition = subMenu.getPosition().replace(dbMenu.getPosition(),menuDto.getPosition());
                    subMenu.setPosition(subPosition);
                    //  计算层级
                    int subLayer = subMenu.getLayerNumber() + layerCha;
                    subMenu.setLayerNumber(subLayer);
                });
                //  获取旧的父节点
                Menu oldParentMenu = this.selectById(dbMenu.getPid());
                oldParentMenu.setClientCount(oldParentMenu.getClientCount() - 1);

                //  获取旧的菜单排序值大于原排序值的菜单列表
                List<Menu> oldNextMenuList = this.selectBySortGreaterThan(dbMenu);
                oldNextMenuList.forEach(menu -> menu.setSort(menu.getSort() - 1));

                //  生成需要修改的菜单列表
                List<Menu> updateMenuList = new ArrayList<>(subMenuList);
                updateMenuList.add(parentMenu);
                updateMenuList.add(oldParentMenu);
                updateMenuList.addAll(oldNextMenuList);
                //  保存修改
                this.updateBatchById(updateMenuList);
            }
            return new ResultBean<>(ResultEnum.SUCCESS,this.updateById(menuDto));
        });
    }

    /**
     * 功能描述: 重写父类方法，根据Id批量修改,修改后删除缓存
     * @param entityList 菜单列表
     * @return boolean { true：成功，false：失败 }
     *
     * date:  2018/11/4 23:14
     */
    @Override
    public boolean updateBatchById(List<Menu> entityList) {
        boolean result =  super.updateBatchById(entityList);
        HashOperations<String, String, String> hashOperations = this.redisTemplate.opsForHash();
        entityList.forEach(item -> hashOperations.delete(MENU_CACHE_NAME,item.toString()));
        return result;
    }

    /**
     * 功能描述: 根据某个菜单的存储位置获取他的全部子节点
     * @param position 存储位置
     * @return List<Menu>
     *
     * date:  2018/11/4 22:37
     */
    private List<Menu> selectSubMenuListByPosition(String position){
        //  创建查询信息
        Wrapper<Menu> wrapper = new EntityWrapper<>();
        wrapper.like("s_position",position, SqlLike.RIGHT);
        //  过滤删除
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return this.selectList(wrapper);
    }

    /**
     * 功能描述: 操作菜单前,会对数据先进行校验
     * @param menuDto 菜单数据
     * @param menuManager 操作接口的实现
     * @return ResultBean<Boolean> { true：成功，false：失败 }
     *
     * date:  2018/11/4 20:13
     */
    private ResultBean<Boolean> operationMenu(MenuDto menuDto,MenuManager menuManager){
        //  判断菜单类
        int folderType = 3;
        if (!menuDto.getType().equals(folderType)){
            if (menuDto.getType().equals(OperationConstant.ONE)){
                Assert.hasText(menuDto.getToken(),"Token不能为空");
                Assert.hasText(menuDto.getPath(),"菜单路径不能为空");
            }else {
                Assert.hasText(menuDto.getToken(),"Token不能为空");
            }
            //  校验token是否存在
            if (menuDto.isCheckToken()){
                boolean isExist = this.checkTokenIsExist(menuDto.getToken());
                AssertUtil.isFalse(isExist,"Token已存在");
            }
        }else {
            menuDto.setToken("");
        }
        return menuManager.operationMenu(menuDto);
    }

    private interface MenuManager{

        /**
         * 操作菜单接口
         * @param menuDto 菜单信息
         * @return ResultBean<Boolean> { true：成功，false：失败 }
         *
         * date:  2018/11/4 19:48
         */
        ResultBean<Boolean> operationMenu(MenuDto menuDto);

    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ResultBean<Boolean> deleteMenu(MenuDto menuDto) {
        //  获取当前菜单信息
        Menu menu = this.selectById(menuDto.getId());
        Assert.notNull(menu,"菜单的Id不存在");
        //  判断当前菜单下是否含有子菜单
        if (menu.getClientCount() > 0){
            return new ResultBean<>(PermissionsEnum.MENU_HAVE_CLIENT);
        }
        //  标记为删除状态
        menu.setStatus(OperationConstant.MINUS_ONE);
        //  获取父级菜单
        Menu parentMenu = this.selectById(menu.getPid());
        Assert.notNull(parentMenu,"父级菜单Id不存在");
        //  子菜单总数 - 1
        parentMenu.setClientCount(parentMenu.getClientCount() - 1);
        //  获取排序值比自己大的菜单列表
        List<Menu> nextMenuList = this.selectBySortGreaterThan(menu);
        nextMenuList.forEach(nextMenu -> nextMenu.setSort(nextMenu.getSort() - 1));

        //  生成需要修改的菜单列表
        List<Menu> updateMenuList = new ArrayList<>(nextMenuList);
        updateMenuList.add(menu);
        updateMenuList.add(parentMenu);
        boolean isDelete = this.updateBatchById(updateMenuList);
        //  删除关联信息
        if (isDelete){
            Wrapper<RoleMenu> wrapper = new EntityWrapper<>();
            wrapper.eq("l_menu_id",menu.getId());
            this.roleMenuMapper.delete(wrapper);
        }
        //  保存修改
        return new ResultBean<>(ResultEnum.SUCCESS,isDelete);
    }

    @Override
    public List<Menu> getMenuWithTree(Long rootId) {
        if (rootId == null){
            rootId = 0L;
        }
        List<Menu> menus = this.getListByPid(rootId);
        getTree(menus);
        return menus;
    }

    public void getTree(List<Menu> menus){
        if (menus != null && menus.size() > 0){
            menus.forEach(item -> {
                List<Menu> listByPid = this.getListByPid(item.getId());
                this.getTree(listByPid);
                item.setSubList(listByPid);
            });
        }
    }

    @Override
    public List<Menu> getListByPid(Long pid) {
        //  设置查询条件
        Wrapper<Menu> wrapper = new EntityWrapper<>();
        //  指定父节点
        wrapper.eq("l_pid", pid);
        //  指定排序值
        wrapper.orderBy("i_sort", true);
        //  过滤已删除的
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return this.selectList(wrapper);
    }

    /**
     * 功能描述: 查询sort 大于指定排序值的菜单列表
     * @param menu 菜单信息 { sort：排序值不能为空,pId：不能为空 }
     * @return List<Menu>
     *
     *
     */
    private List<Menu> selectBySortGreaterThan(Menu menu){
        //  设置查询条件
        Wrapper<Menu> wrapper = new EntityWrapper<>();
        //  指定父节点
        wrapper.eq("l_pid",menu.getPid());
        //  指定排序值
        wrapper.gt("i_sort",menu.getSort());
        //  过滤已删除的
        wrapper.notIn("i_status",OperationConstant.MINUS_ONE);
        return this.selectList(wrapper);
    }
}
