package com.microservices.system.service.impl;

import com.microservices.common.core.constant.UserConstants;
import com.microservices.common.core.enums.SystemRole;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.uuid.IdUtils;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.domain.SysDept;
import com.microservices.system.api.domain.SysRole;
import com.microservices.system.api.domain.SysUserDeptRole;
import com.microservices.system.domain.SysDeptRole;
import com.microservices.system.domain.SysMenu;
import com.microservices.system.domain.vo.SysRoleInputVo;
import com.microservices.system.mapper.SysDeptMapper;
import com.microservices.system.mapper.SysDeptRoleMapper;
import com.microservices.system.mapper.SysUserDeptRoleMapper;
import com.microservices.system.service.ISysDeptRoleService;
import com.microservices.system.service.ISysMenuService;
import com.microservices.system.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 部门下可选角色Service业务层处理
 *
 * @author otto
 * @date 2023-04-12
 */
@Service
public class SysDeptRoleServiceImpl implements ISysDeptRoleService {
    @Autowired
    private SysDeptRoleMapper sysDeptRoleMapper;
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private SysUserDeptRoleMapper sysUserDeptRoleMapper;
    @Autowired
    private ISysMenuService sysMenuService;
    @Autowired
    private ISysMenuService menuService;

    @Override
    public SysDept selectSysDeptById(Long deptId) {
        SysDept sysDept = sysDeptMapper.selectDeptById(deptId);
        if (sysDept == null) {
            throw new ServiceException("该部门不存在(部门Id[" + deptId + "]");
        }
        return sysDept;
    }

    @Override
    public List<SysRole> selectSysRoleList() {
        List<SysRole> roleList = roleService.selectRoleList(new SysRole());
        // 虚拟角色不允许选择
        return roleList.stream().filter(x -> !SystemRole.isVirtual(x.getRoleKey())).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSysRoleForSysDept(Long currentDeptId, Long roleId) {
        List<SysRole> roleList = selectSysRoleListByDept(currentDeptId, null);
        List<Long> roleIdList = roleList.stream().map(SysRole::getRoleId).collect(Collectors.toList());
        roleIdList.add(roleId);
        addSysRoleListForSysDept(currentDeptId, roleIdList.toArray(new Long[0]));
    }

    @Override
    public List<SysRole> selectSysRoleListByDept(Long deptId, String roleName) {
        List<SysDeptRole> deptRoleList = sysDeptRoleMapper.selectSysDeptRoleListByDeptId(deptId, roleName);
        List<Long> roleIds = deptRoleList.stream().map(SysDeptRole::getRoleId).collect(Collectors.toList());
        List<SysRole> roleList = new ArrayList<>();
        if (!roleIds.isEmpty()) {
            roleList = roleService.selectRoleListInIds(roleIds);
            roleList.forEach(x -> {
                x.setIsDefault(isDefaultRole(x.getRoleKey()));
            });
        }
        return roleList
                .stream()
                .sorted(Comparator.comparing(SysRole::getRoleSort))
                .filter(x -> !SystemRole.isVirtual(x.getRoleKey()))
                .collect(Collectors.toList());
    }

    public Boolean isDefaultRole(String roleKey) {
        return SystemRole.isDefault(roleKey) && !SecurityUtils.isAdmin();
    }

    @Override
    public String getRoleDataScopeRole(Long[] menuIds) {
        List<SysMenu> sysMenuList = sysMenuService.selectMenuListByMenuIds(menuIds);
        if (sysMenuList.stream().anyMatch(x -> x.getPerms().equals(UserConstants.PMS_PROJECT_DATA_SCOPE_PERMS_ALL))) {
            return UserConstants.DATA_SCOPE_DEPT_AND_CHILD;
        } else if (sysMenuList.stream().anyMatch(x -> x.getPerms().equals(UserConstants.PMS_PROJECT_DATA_SCOPE_PERMS_ME))) {
            return UserConstants.DATA_SCOPE_SELF;
        } else {
            return UserConstants.DATA_SCOPE_DEPT_AND_CHILD;
        }
    }

    @Override
    public String genRoleKeyByDeptId(Long deptId) {
        String roleKey = null;
        boolean isExist = true;
        while (isExist) {
            roleKey = String.format("%s_%d", IdUtils.fastShortUUID(), deptId);
            SysRole sysRole = roleService.selectRoleByKey(roleKey);
            if (sysRole == null) {
                isExist = false;
            }
        }
        return roleKey;
    }

    @Override
    public void checkDeptHasRoleName(Long deptId, String roleName) {
        List<SysRole> roleList = sysDeptRoleMapper.selectSysDeptRoleByDeptIdAndRoleName(deptId, roleName);
        if (!roleList.isEmpty()) {
            throw new ServiceException("该角色名称已存在，请重新输入(角色名称[%s])", roleName);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addSysRole(Long deptId, SysRoleInputVo roleInput) {
        SysRole role = roleInput.toSysRole();
        if (role.getRoleSort() == null) {
            role.setRoleSort(1);
        }
        role.setDeptType(2);
        role.setCreateBy(SecurityUtils.getUsername());
        role.setStatus("0");
        checkDeptHasRoleName(deptId, roleInput.getRoleName());

        // 项目管理下角色标识自动生成
        role.setRoleKey(genRoleKeyByDeptId(deptId));
        int success = roleService.insertRole(role);
        role = roleService.selectRoleByKey(role.getRoleKey());
        addSysRoleForSysDept(deptId, role.getRoleId());
        return success;
    }

    @Override
    public int updateSysRole(Long deptId, SysRole role) {
        SysRole oldRole = roleService.selectRoleById(role.getRoleId());
        if (isDefaultRole(oldRole.getRoleKey())) {
            throw new ServiceException("默认角色不允许修改");
        }
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        if (StringUtils.isNotEmpty(role.getRoleName()) && !oldRole.getRoleName().equals(role.getRoleName())) {
            checkDeptHasRoleName(deptId, role.getRoleName());
        }
        role.setUpdateBy(SecurityUtils.getUsername());
        // 设置项目管理项目的数据权限
        role.setDataScope(getRoleDataScopeRole(role.getMenuIds()));
        return roleService.updateRole(role);
    }

    @Override
    public AjaxResult roleMenuTreeSelect(Long currentDeptId, Long roleId) {
        List<SysMenu> menus = menuService.selectMenuList(new SysMenu());
        AjaxResult ajax = AjaxResult.success();
        List<Long> checkedKeys;
        if (roleId != null) {
            checkedKeys = menuService.selectMenuListByRoleId(roleId);
        } else {
            checkedKeys = new ArrayList<>();
        }
        ajax.put("checkedKeys", checkedKeys);
        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
        return ajax;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addSysRoleListForSysDept(Long deptId, Long[] roleIds) {
        selectSysDeptById(deptId);
        List<Long> newRoleIdList = Arrays.asList(roleIds);
        List<SysDeptRole> oldDeptRoleList = sysDeptRoleMapper.selectSysDeptRoleListByDeptId(deptId, null);

        List<Long> addRoleIdList = new ArrayList<>(newRoleIdList);
        //若原角色列表不为空则需删除此次请求未选择的角色
        if (oldDeptRoleList != null && !oldDeptRoleList.isEmpty()) {
            List<Long> oldRoleIdList = oldDeptRoleList
                    .stream()
                    .filter(x -> {
                        SysRole sysRole = x.getSysRole();
                        if (sysRole != null) {
                            return !SystemRole.isVirtual(sysRole.getRoleKey());
                        }
                        return false;
                    })
                    .map(SysDeptRole::getRoleId)
                    .collect(Collectors.toList());

            //需要新增的id列表为新增加的id列表减掉已存在的id列表
            addRoleIdList.removeAll(oldRoleIdList);
            //需要删除的id列表为已存在的id列表减掉新增加的id列表
            List<Long> delRoleIdList = new ArrayList<>(oldRoleIdList);
            delRoleIdList.removeAll(newRoleIdList);
            if (!delRoleIdList.isEmpty()) {
                deleteSysRoleListForSysDept(deptId, delRoleIdList.toArray(new Long[0]));
            }
        }

        for (Long roleId : addRoleIdList) {
            roleService.selectRoleById(roleId);
            SysDeptRole sysDeptRole = sysDeptRoleMapper
                    .selectSysDeptRoleByDeptIdAndRoleId(deptId, roleId);
            if (sysDeptRole == null) {
                sysDeptRole = new SysDeptRole(deptId, roleId);
                sysDeptRoleMapper.insertSysDeptRole(sysDeptRole);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteSysRoleListForSysDept(Long deptId, Long[] roleIds) {
        SysDept sysDept = selectSysDeptById(deptId);
        for (Long roleId : roleIds) {
            SysDeptRole sysDeptRole = sysDeptRoleMapper.selectSysDeptRoleByDeptIdAndRoleId(deptId, roleId);
            if (sysDeptRole == null) {
//                throw new ServiceException("部门下不存在该可选角色(部门名称[" + sysDept.getDeptName() + "],角色Id[" + roleId + "])");
                continue;
            }

            List<SysUserDeptRole> userIdentifyList = sysUserDeptRoleMapper.selectSysUserDeptRoleByDeptIdAndRoleId(deptId, roleId);
            if (!userIdentifyList.isEmpty()) {
                String usersStr = userIdentifyList.stream().map(x -> x.getSysUser().getUserName()).collect(Collectors.joining(","));
                SysRole sysRole = roleService.selectRoleById(roleId);
                throw new ServiceException("部门下该可选角色已被用户关联(部门名称[" + sysDept.getDeptName() + "],角色名[" + sysRole.getRoleName() + "],用户名[" + usersStr + "])");
            }
        }
        return sysDeptRoleMapper.deleteSysRoleListForSysDept(deptId, roleIds);
    }

    @Override
    public boolean checkDeptHasRole(Long deptId, Long roleId) {
        SysDeptRole sysDeptRole = sysDeptRoleMapper.selectSysDeptRoleByDeptIdAndRoleId(deptId, roleId);
        return sysDeptRole != null;
    }
}
