package com.njcn.product.system.theme.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.constant.SecurityConstants;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.product.auth.pojo.constant.FunctionState;
import com.njcn.product.auth.pojo.constant.UserType;
import com.njcn.product.auth.pojo.enums.UserResponseEnum;
import com.njcn.product.auth.pojo.po.Role;
import com.njcn.product.auth.service.IRoleService;
import com.njcn.product.auth.service.IUserRoleService;
import com.njcn.product.system.theme.mapper.FunctionMapper;
import com.njcn.product.system.theme.mapper.RoleFunctionMapper;
import com.njcn.product.system.theme.pojo.param.FunctionParam;
import com.njcn.product.system.theme.pojo.param.RoleParam;
import com.njcn.product.system.theme.pojo.po.Function;
import com.njcn.product.system.theme.pojo.po.HomePage;
import com.njcn.product.system.theme.pojo.po.RoleFunction;
import com.njcn.product.system.theme.pojo.vo.FunctionVO;
import com.njcn.product.system.theme.service.IFunctionService;
import com.njcn.product.system.theme.service.IHomePageService;
import com.njcn.product.system.theme.service.IRoleFunctionService;
import com.njcn.redis.pojo.enums.RedisKeyEnum;
import com.njcn.redis.utils.RedisUtil;
import com.njcn.product.auth.pojo.po.UserRole;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hongawen
 * @since 2021-12-13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FunctionServiceImpl extends ServiceImpl<FunctionMapper, Function> implements IFunctionService {

    private final RedisUtil redisUtil;

    private final IRoleService roleService;

    private final IRoleFunctionService roleFunctionService;

    private final FunctionMapper functionMapper;

    private final IUserRoleService userRoleService;

    private final IHomePageService homePageService;

    private final RoleFunctionMapper roleFunctionMapper;

    /**
     * 将系统中角色--资源对应数据缓存到redis
     * 先清除，再缓存
     */
    @Override
    public void refreshRolesFunctionsCache() {
        redisUtil.delete(RedisKeyEnum.ROLE_FUNCTION_KEY.getKey());
        redisUtil.delete(RedisKeyEnum.PUBLIC_FUNCTIONS_KEY.getKey());
        //缓存公共资源
        List<Function> publicFunctions = lambdaQuery()
                .eq(Function::getType, 2)
                .eq(Function::getState, DataStateEnum.ENABLE.getCode())
                .list();
        redisUtil.saveByKey(
                RedisKeyEnum.PUBLIC_FUNCTIONS_KEY.getKey()
                , publicFunctions.stream().map(Function::getPath).collect(Collectors.toList())
        );

        //缓存每个角色对应的资源
        Map<String, List<String>> roleFunctionInfo = new HashMap<>(8);
        List<Role> roles = roleService.lambdaQuery()
                .eq(Role::getState, DataStateEnum.ENABLE.getCode())
                .list();

        roles.forEach(role -> {
            //根据当前角色列表获取其所对应的所有资源
            List<RoleFunction> roleFunctions = roleFunctionService.lambdaQuery()
                    .eq(RoleFunction::getRoleId, role.getId())
                    .list();
            //根据角色权限关系表获取权限的uri
            if (CollectionUtil.isEmpty(roleFunctions)) {
                roleFunctionInfo.put(SecurityConstants.AUTHORITY_PREFIX + role.getCode(), null);
            } else {
                List<Function> functions = lambdaQuery()
                        .in(Function::getId, roleFunctions.stream().map(RoleFunction::getFunctionId).collect(Collectors.toList()))
                        .eq(Function::getState, DataStateEnum.ENABLE.getCode())
                        .list();
                roleFunctionInfo.put(SecurityConstants.AUTHORITY_PREFIX + role.getCode(), functions.stream().map(Function::getPath).collect(Collectors.toList()));
            }
        });
        redisUtil.saveByKey(RedisKeyEnum.ROLE_FUNCTION_KEY.getKey(), roleFunctionInfo);
    }

    @Override
    public boolean addFunction(FunctionParam functionParam) {
        checkFunctionParam(functionParam, false);
        Function function = new Function();
        BeanUtil.copyProperties(functionParam, function);
        function.setState(FunctionState.ENABLE);
        if (Objects.equals(functionParam.getPid(), FunctionState.FATHER_PID)) {
            function.setPids(FunctionState.FATHER_PID);
        } else {
            Function fatherFaction = this.lambdaQuery().eq(Function::getId, functionParam.getPid()).one();
            if (Objects.equals(fatherFaction.getPid(), FunctionState.FATHER_PID)) {
                function.setPids(functionParam.getPid());
            } else {
                String pidS = fatherFaction.getPids();
                function.setPids(pidS + "," + functionParam.getPid());
            }
        }
        boolean result = this.save(function);
        if (result) {
            //刷新redis里面的资源权限
            refreshRolesFunctionsCache();
        }
        return result;
    }

    @Override
    public boolean updateFunction(FunctionParam.FunctionUpdateParam functionParam) {
        checkFunctionParam(functionParam, true);
        Function function = new Function();
        BeanUtil.copyProperties(functionParam, function);
        boolean result = this.updateById(function);
        if (result) {
            refreshRolesFunctionsCache();
        }
        return result;
    }

    @Override
    public void deleteFunction(String id) {
        boolean result;
        List<Function> list = this.lambdaQuery().eq(Function::getState, FunctionState.ENABLE).eq(Function::getPid, id).list();
        if (CollectionUtils.isEmpty(list)) {
            result = this.lambdaUpdate()
                    .set(Function::getState, FunctionState.DELETE)
                    .in(Function::getId, id)
                    .update();
            if (result) {
                refreshRolesFunctionsCache();
            }
        } else {
            throw new BusinessException(UserResponseEnum.BINDING_BUTTON);
        }
    }

    @Override
    public List<FunctionVO> getFunctionTree() {
        List<FunctionVO> list = functionMapper.getAllFunctions();
        return list.stream()
                .filter(fun -> Objects.equals(FunctionState.FATHER_PID, fun.getPid()))
                .peek(funS -> funS.setChildren(getChildCategoryList(funS, list)))
                .sorted(Comparator.comparingInt(FunctionVO::getSort))
                .collect(Collectors.toList());
    }

    @Override
    public Function getFunctionById(String id) {
        return this.lambdaQuery().eq(Function::getId, id).one();
    }

    @Override
    public List<Function> getButtonsById(String id) {
        List<Integer> typeList = Arrays.asList(FunctionState.BUTTON, FunctionState.PUBLIC, FunctionState.TAB);
        return this.lambdaQuery().eq(Function::getPid, id).in(Function::getType, typeList).eq(Function::getState, FunctionState.ENABLE).orderByAsc(Function::getSort).list();
    }

    @Override
    public List<FunctionVO> getRouteMenu() {
        List<FunctionVO> result = new ArrayList<>();
        List<String> functionList;
        if (Objects.equals(RequestUtil.getUsername(), UserType.SUPER_ADMIN)) {
            //查询所有菜单
            functionList = this.lambdaQuery().eq(Function::getState, FunctionState.ENABLE).list().stream().map(Function::getId).distinct().collect(Collectors.toList());
        } else {
            List<String> roleList = userRoleService.getUserRoleByUserId(RequestUtil.getUserIndex()).stream().map(UserRole::getRoleId).distinct().collect(Collectors.toList());
            functionList = roleFunctionService.getFunctionsByList(roleList);
        }
        if (CollectionUtils.isEmpty(functionList)) {
            return result;
        }
        List<FunctionVO> functionVOList = functionMapper.getFunctionsByList(functionList);
        result = functionVOList.stream()
                .filter(fun -> Objects.equals(FunctionState.FATHER_PID, fun.getPid().trim()))
                .peek(funS -> funS.setChildren(getChildCategoryList(funS, functionVOList)))
                .sorted(Comparator.comparingInt(FunctionVO::getSort))
                .collect(Collectors.toList());
        //组装驾驶舱
        setDriverChildren(result);
        //处理tab页
        setTab(result);
        return result;
    }

    @Override
    public Boolean updateRoleComponent(RoleParam.RoleFunctionComponent roleFunctionComponent) {
        deleteComponentsByRoleIndex(roleFunctionComponent.getId());
        if (!roleFunctionComponent.getIdList().isEmpty()) {
            List<RoleFunction> list = new ArrayList<>();
            RoleFunction roleFunction;
            for (String pojo : roleFunctionComponent.getIdList()) {
                roleFunction = new RoleFunction();
                roleFunction.setRoleId(roleFunctionComponent.getId());
                roleFunction.setFunctionId(pojo);
                list.add(roleFunction);
            }
            roleFunctionService.saveBatch(list);
        }
        refreshRolesFunctionsCache();
        return true;
    }

    @Override
    public List<FunctionVO> getUserFunctionTree() {
        List<FunctionVO> result;
        List<String> functionList;
        if (Objects.equals(RequestUtil.getUsername(), UserType.SUPER_ADMIN)) {
            //查询所有菜单
            functionList = this.lambdaQuery().eq(Function::getState, FunctionState.ENABLE).list().stream().map(Function::getId).distinct().collect(Collectors.toList());
        } else {
            List<String> roleList = userRoleService.getUserRoleByUserId(RequestUtil.getUserIndex()).stream().map(UserRole::getRoleId).distinct().collect(Collectors.toList());
            functionList = roleFunctionService.getFunctionsByList(roleList);
        }
        List<FunctionVO> functionVOList = functionMapper.getUserFunctionsByList(functionList);
        result = functionVOList.stream()
                .filter(fun -> Objects.equals(FunctionState.FATHER_PID, fun.getPid()))
                .peek(funS -> funS.setChildren(getChildCategoryList(funS, functionVOList)))
                .sorted(Comparator.comparingInt(FunctionVO::getSort))
                .collect(Collectors.toList());
        //组装驾驶舱
        setDriverChildren(result);
        return result;
    }

    @Override
    public List<Function> getFunctionByList(List<String> list) {
        return this.lambdaQuery().in(Function::getId, list).list();
    }

    /**
     * 根据角色删除资源
     *
     * @param roleIndex 角色索引
     */
    public void deleteComponentsByRoleIndex(String roleIndex) {
        QueryWrapper<RoleFunction> roleFunctionQueryWrapper = new QueryWrapper<>();
        roleFunctionQueryWrapper.eq("sys_role_function.role_id", roleIndex);
        roleFunctionMapper.delete(roleFunctionQueryWrapper);
    }

    /**
     * 根据当前分类找出子类，递归找出子类的子类
     */
    private List<FunctionVO> getChildCategoryList(FunctionVO currMenu, List<FunctionVO> categories) {
        return categories.stream().filter(o -> Objects.equals(o.getPid(), currMenu.getId()))
                .peek(o -> o.setChildren(getChildCategoryList(o, categories)))
                .sorted(Comparator.comparingInt(FunctionVO::getSort))
                .collect(Collectors.toList());
    }

    /**
     * 组装驾驶舱子级
     *
     * @param list 菜单集合
     */
    private void setDriverChildren(List<FunctionVO> list) {
        List<HomePage> homePages = homePageService.getHomePagesByUserId(RequestUtil.getUserIndex());
        list.forEach(item -> {
            if (Objects.equals(item.getRoutePath(), FunctionState.DRIVER_NAME)) {
                homePages.forEach(po -> {
                    FunctionVO functionVO = new FunctionVO();
                    functionVO.setId(po.getId());
                    functionVO.setPid(item.getId());
                    functionVO.setTitle(po.getName());
                    functionVO.setCode(item.getCode());
                    functionVO.setRouteName(po.getPath().substring(po.getPath().lastIndexOf("/") + 1));
                    functionVO.setRoutePath(po.getPath());
                    functionVO.setIcon(po.getIcon());
                    functionVO.setSort(po.getSort());
                    functionVO.setType(item.getType());
                    functionVO.setRemark(po.getName());
                    functionVO.setChildren(new ArrayList<>());
                    item.getChildren().add(functionVO);
                });
            }
        });
    }

    /**
     * 处理tab页
     */
    private void setTab(List<FunctionVO> list) {
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(item -> {
                List<FunctionVO> children = item.getChildren();
                if (!CollectionUtils.isEmpty(children)) {
                    for (FunctionVO child : children) {
                        List<FunctionVO> children2 = child.getChildren();
                        if (!CollectionUtils.isEmpty(children2)) {
                            setTab(children2);
                        } else if (Objects.equals(child.getType(), 4)) {
                            item.setUserTab(item.getChildren());
                            item.setChildren(new ArrayList<>());
                            break;
                        }
                    }
                }
            });
        }
    }

    /**
     * 校验参数，
     * 1.检查是否存在相同名称的菜单
     * 名称 && 路径做唯一判断
     */
    private void checkFunctionParam(FunctionParam functionParam, boolean isExcludeSelf) {
        LambdaQueryWrapper<Function> functionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        functionLambdaQueryWrapper
                .eq(Function::getName, functionParam.getName())
                .eq(Function::getPath, functionParam.getPath())
                .eq(Function::getPid, functionParam.getPid())
                .eq(Function::getState, FunctionState.ENABLE);
        //更新的时候，需排除当前记录
        if (isExcludeSelf) {
            if (functionParam instanceof FunctionParam.FunctionUpdateParam) {
                functionLambdaQueryWrapper.ne(Function::getId, ((FunctionParam.FunctionUpdateParam) functionParam).getId());
            }
        }
        int countByAccount = this.count(functionLambdaQueryWrapper);
        //大于等于1个则表示重复
        if (countByAccount >= 1) {
            throw new BusinessException(UserResponseEnum.FUNCTION_PATH_EXIST);
        }
    }

}
