package com.qmth.teachcloud.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.teachcloud.common.bean.dto.DataPermissionInfo;
import com.qmth.teachcloud.common.bean.params.SysRolePrivilegeParams;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.FieldUniqueEnum;
import com.qmth.teachcloud.common.enums.RoleSourceEnum;
import com.qmth.teachcloud.common.enums.RoleTypeEnum;
import com.qmth.teachcloud.common.mapper.SysRoleMapper;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
    private final static Logger log = LoggerFactory.getLogger(SysRoleServiceImpl.class);

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private SysRolePrivilegeService sysRolePrivilegeService;

    @Resource
    private CommonCacheService commonCacheService;

    @Resource
    SysRoleService sysRoleService;

    @Resource
    TeachcloudCommonService commonService;

    @Resource
    SysPrivilegeService sysPrivilegeService;

    @Resource
    SysRoleGroupMemberService sysRoleGroupMemberService;

    @Resource
    private BasicRoleDataPermissionService basicRoleDataPermissionService;

    @Override
    public IPage<SysRole> list(String name, Boolean enable, Integer pageNumber, Integer pageSize) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        return this.baseMapper.listPage(new Page<>(pageNumber, pageSize), schoolId, SystemConstant.translateSpecificSign(name), enable);
    }

    /**
     * 新增角色
     *
     * @param sysRolePrivilegeParams
     * @return
     */
    @Transactional
    @Override
    public boolean saveRoleNew(SysRolePrivilegeParams sysRolePrivilegeParams) {
        try {
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            Long schoolId = Objects.nonNull(ServletUtil.getRequestHeaderSchoolIdByNotVaild()) ? Long.valueOf(ServletUtil.getRequestHeaderSchoolIdByNotVaild().toString()) : null;

            // 组装角色
            SysRole role = new SysRole();
            role.setId(sysRolePrivilegeParams.getId());
            role.setName(sysRolePrivilegeParams.getName());
            role.setPrivilegeIds(sysRolePrivilegeParams.getPrivilegeIds().stream().map(SystemConstant::convertIdToLong).toArray(Long[]::new));
            role.setDefaultRole(sysRolePrivilegeParams.getDefaultRole() == null ? false : sysRolePrivilegeParams.getDefaultRole());
            // 内置角色学校为null
            role.setSchoolId(role.getDefaultRole() ? null : schoolId);
            List<SysRole> sysRoleList = this.list();
            // 内置角色检验
            if (role.getDefaultRole()) {
                if (schoolId == null) {
                    // 校验在内置角色中是否存在相同名称角色
                    List<SysRole> defaultRoleList = sysRoleList.stream().filter(m -> m.getSchoolId() == null && m.getName().equals(role.getName()) && !m.getId().equals(role.getId())).distinct().collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(defaultRoleList)) {
                        throw ExceptionResultEnum.ERROR.exception("内置角色中已存在名称[" + role.getName() + "]的角色");
                    }
                    // 校验在学校角色中是否存在相同名称角色
                    List<SysRole> schoolRoleList = sysRoleList.stream().filter(m -> m.getSchoolId() != null && m.getName().equals(role.getName())).distinct().collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(schoolRoleList)) {
                        throw ExceptionResultEnum.ERROR.exception("学校角色中已存在名称[" + role.getName() + "]的角色");
                    }
                }
            } else {
                // 校验在内置角色中是否存在相同名称角色
                List<SysRole> defaultRoleList = sysRoleList.stream().filter(m -> m.getSchoolId() == null && m.getName().equals(role.getName())).distinct().collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(defaultRoleList)) {
                    throw ExceptionResultEnum.ERROR.exception("内置角色中已存在名称[" + role.getName() + "]的角色");
                }
                // 校验在学校角色中是否存在相同名称角色
                List<SysRole> schoolRoleList = sysRoleList.stream().filter(m -> schoolId.equals(m.getSchoolId()) && m.getName().equals(role.getName()) && !m.getId().equals(role.getId())).distinct().collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(schoolRoleList)) {
                    throw ExceptionResultEnum.ERROR.exception("学校角色中已存在名称[" + role.getName() + "]的角色");
                }
            }

            //编辑
            if (Objects.nonNull(role.getId())) {
                if (!role.getDefaultRole()) {
                    role.setInterpret(sysRolePrivilegeParams.getInterpret());
                }
                role.updateInfo(sysUser.getId());
                sysRoleService.updateById(role);
                commonCacheService.updateRoleCache(role.getId());
                boolean isChange = sysRolePrivilegeService.saveBatch(role);//角色权限
                if (isChange) {
//                    commonCacheService.removeRolePrivilegeCache(schoolId, role.getId());
                    //绑定该角色的用户都需要清除鉴权缓存
                    List<SysUserRole> sysUserRoleList = sysUserRoleService.listByRoleId(role.getId());
                    commonService.removeUserInfoBatch(sysUserRoleList.stream().map(SysUserRole::getUserId).collect(Collectors.toList()), true);
                }
            } else {
                role.insertInfo(sysUser.getId());
                if (role.getDefaultRole()) {
                    role.setSource(RoleSourceEnum.SYS);
                    role.setInterpret("系统内置");
                } else {
                    role.setInterpret(sysRolePrivilegeParams.getInterpret());
                }
                sysRoleService.save(role);
                sysRolePrivilegeService.saveBatch(role);//角色权限
                commonCacheService.updateCustomRoleCache();
            }

            // 组装角色权限
            Long roleId = role.getId();
            // 数据权限集合
            List<DataPermissionInfo> dataPermissionInfoList = sysRolePrivilegeParams.getDataPermissionInfo();
            basicRoleDataPermissionService.saveRoleDataPermission(roleId, dataPermissionInfoList);
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return true;
    }

    @Transactional
    @Override
    public boolean saveRoleReportNew(SysRole role) {
        try {
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            Long schoolId = Objects.nonNull(ServletUtil.getRequestHeaderSchoolIdByNotVaild()) ? Long.valueOf(ServletUtil.getRequestHeaderSchoolIdByNotVaild().toString()) : null;
            if (Objects.isNull(schoolId) && Objects.isNull(role.getSchoolId())) {
                role.setSchoolId(sysUser.getSchoolId());
            } else if (Objects.nonNull(schoolId)) {
                role.setSchoolId(schoolId);
            }
            // 系统默认内置角色（不允许和系统默认角色重名）
            List<String> defaultRoleNames = this.list(new QueryWrapper<SysRole>().lambda()
                            .eq(SysRole::getDefaultRole, true)
                            .eq(SysRole::getEnable, true))
                    .stream()
                    .map(SysRole::getName)
                    .distinct()
                    .collect(Collectors.toList());
            String roleName = role.getName();
            if (defaultRoleNames.contains(roleName)) {
                throw ExceptionResultEnum.ERROR.exception("自定义角色名称不能和系统默认角色[" + roleName + "]同名");
            }
            if (Objects.nonNull(role.getId())) {//编辑
                List<SysRolePrivilege> sysRolePrivilegeList = commonCacheService.rolePrivilegeCache(sysUser.getSchoolId(), role.getId());
                QueryWrapper<SysPrivilege> sysPrivilegeQueryWrapper = new QueryWrapper<>();
                sysPrivilegeQueryWrapper.lambda().in(SysPrivilege::getId, role.getPrivilegeIds());
                List<SysPrivilege> sysPrivilegeList = sysPrivilegeService.list(sysPrivilegeQueryWrapper);
                Set<String> relatedSet = sysPrivilegeList.stream().filter(s -> Objects.nonNull(s.getRelated())).map(s -> s.getRelated()).collect(Collectors.toSet());
                Set<Long> finalRelatedSet = new HashSet<>(Arrays.asList(role.getPrivilegeIds()));
                for (String s : relatedSet) {
                    if (s.contains(",")) {
                        String[] arrays = s.split(",");
                        for (int i = 0; i < arrays.length; i++) {
                            finalRelatedSet.add(Long.parseLong(arrays[i].trim()));
                        }
                    } else {
                        finalRelatedSet.add(Long.parseLong(s));
                    }
                }
                List<Long> relatedList = new ArrayList<>(finalRelatedSet);
                int count = (int) sysRolePrivilegeList.stream().filter(s -> relatedList.contains(s.getPrivilegeId())).count();
                role.updateInfo(sysUser.getId());
                sysRoleService.updateById(role);
                commonCacheService.updateRoleCache(role.getId());

                if (count != sysRolePrivilegeList.size() || count != finalRelatedSet.size()) {
                    // 删除权限前先更新涉及特殊权限用户 -> 需要重新同步
                    sysRolePrivilegeService.removeByRoleId(role.getId());
                    sysRolePrivilegeService.saveBatch(role);//角色权限
//                    commonCacheService.updateRolePrivilegeCache(schoolId, role.getId());
                    //绑定该角色的用户都需要清除鉴权缓存
                    List<SysUserRole> sysUserRoleList = sysUserRoleService.listByRoleId(role.getId());
                    commonService.removeUserInfoBatch(sysUserRoleList.stream().map(s -> s.getUserId()).collect(Collectors.toList()), true);
                }
            } else {
                role.insertInfo(sysUser.getId());
                sysRoleService.save(role);
                sysRolePrivilegeService.saveBatch(role);//角色权限
            }
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return true;
    }

    @Override
    public boolean enable(SysRole role) throws NoSuchAlgorithmException {
        UpdateWrapper<SysRole> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(SysRole::getEnable, role.getEnable()).eq(SysRole::getId, role.getId());
        this.update(updateWrapper);

        UpdateWrapper<SysRoleGroupMember> sysRoleGroupMemberUpdateWrapper = new UpdateWrapper<>();
        sysRoleGroupMemberUpdateWrapper.lambda().eq(SysRoleGroupMember::getRoleId, role.getId())
                .set(SysRoleGroupMember::getEnable, role.getEnable());
        sysRoleGroupMemberService.update(sysRoleGroupMemberUpdateWrapper);

        commonCacheService.updateRoleCache(role.getId());
        //如果状态为禁用，需要踢下线重新登录
        if (!role.getEnable()) {
            List<SysUserRole> sysUserRoleList = sysUserRoleService.listByRoleId(role.getId());
            for (SysUserRole s : sysUserRoleList) {
                commonService.removeUserInfo(s.getUserId(), true);
            }
        }
        return true;
    }

    @Override
    public boolean enableReport(SysRole role) throws NoSuchAlgorithmException {
        UpdateWrapper<SysRole> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(SysRole::getEnable, role.getEnable()).eq(SysRole::getId, role.getId());
        this.update(updateWrapper);

        commonCacheService.updateRoleCache(role.getId());
        //如果状态为禁用，需要踢下线重新登录
        if (!role.getEnable()) {
            List<SysUserRole> sysUserRoleList = sysUserRoleService.listByRoleId(role.getId());
            for (SysUserRole s : sysUserRoleList) {
                commonService.removeUserInfo(s.getUserId(), true);
            }
        }
        return true;
    }

    @Transactional
    @Override
    public boolean remove(Long id) {
        // 判断绑定用户
        List<SysUserRole> userRoleList = sysUserRoleService.listByRoleId(id);
        if (userRoleList != null && userRoleList.size() > 0) {
            throw ExceptionResultEnum.ERROR.exception("用户绑定，不能删除");
        }
        //删除权限
        sysRolePrivilegeService.removeByRoleId(id);
        this.removeById(id);

        QueryWrapper<SysRoleGroupMember> sysRoleGroupMemberQueryWrapper = new QueryWrapper<>();
        sysRoleGroupMemberQueryWrapper.lambda().eq(SysRoleGroupMember::getRoleId, id);
        sysRoleGroupMemberService.remove(sysRoleGroupMemberQueryWrapper);

        commonCacheService.removeRoleCache(id);
//        commonCacheService.removeRolePrivilegeCache(sysRole.getSchoolId(), id);
        return true;
    }

    @Override
    public boolean removeReport(Long id) {
        // 判断绑定用户
        List<SysUserRole> userRoleList = sysUserRoleService.listByRoleId(id);
        if (userRoleList != null && userRoleList.size() > 0) {
            throw ExceptionResultEnum.ERROR.exception("用户绑定，不能删除");
        }
        //删除权限
        sysRolePrivilegeService.removeByRoleId(id);
        this.removeById(id);

        commonCacheService.removeRoleCache(id);
//        commonCacheService.removeRolePrivilegeCache(sysRole.getSchoolId(), id);
        return true;
    }

    @Override
    public List<Long> listUserRoleIds(Long userId) {
        List<SysRole> sysRoleList = listRolesByUserId(userId);
        return CollectionUtils.isEmpty(sysRoleList) ? new ArrayList<>() : sysRoleList.stream().map(SysRole::getId).collect(Collectors.toList());
    }

    @Override
    public List<SysRole> listRolesByUserId(Long userId) {
        return this.baseMapper.listRolesByUserId(userId);
    }

    @Override
    public List<SysRole> list(Long[] roleIds, String name) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysRole::getType, name).in(SysRole::getId, roleIds);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<SysRole> listToUser() {
        String schoolId = (String) ServletUtil.getRequestHeaderSchoolIdByNotVaild();
//        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
//        queryWrapper.lambda().eq(SysRole::getEnable, true);
//        if (Objects.isNull(object)) {
//            queryWrapper.lambda().isNull(SysRole::getSchoolId);
//        } else {
//            Long schoolId = Long.valueOf(object.toString());
//            queryWrapper.lambda().eq(SysRole::getSchoolId, schoolId);
//        }
        return this.baseMapper.listRolesBySchoolId(Objects.nonNull(schoolId) ? Long.valueOf(schoolId) : null);
    }

    @Override
    public List<SysRole> listToAdmin() {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().isNull(SysRole::getSchoolId)
                .and(s -> s.ne(SysRole::getType, RoleTypeEnum.ADMIN).or().isNull(SysRole::getType))
                .eq(SysRole::getEnable, true)
                .orderByAsc(SysRole::getId);
        return this.list(queryWrapper);
    }

    /**
     * 查询自定义角色
     *
     * @return
     */
    @Override
    public List<SysRole> findCustomRole() {
        QueryWrapper<SysRole> sysRoleQueryWrapper = new QueryWrapper<>();
        sysRoleQueryWrapper.lambda().eq(SysRole::getDefaultRole, true)
                .eq(SysRole::getEnable, true)
                .isNull(SysRole::getSchoolId)
                .and(l -> l.ne(SysRole::getType, SystemConstant.ADMIN_CODE.toUpperCase()).or().isNull(SysRole::getType))
                .orderByAsc(SysRole::getId);
        return sysRoleService.list(sysRoleQueryWrapper);
    }

    @Override
    public boolean builtin(Long id, boolean defaultRole) {
        SysRole sysRole = this.getById(id);
        sysRole.setDefaultRole(defaultRole);

        commonCacheService.removeCustomRoleCache();
        return this.updateById(sysRole);
    }

    @Override
    public List<SysRole> getByRoleName(Long schoolId, String roleName) {
        return this.baseMapper.getByRoleName(schoolId, roleName);
    }

    @Override
    public SysRole getDefaultRoleByType(RoleTypeEnum type) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().isNull(SysRole::getSchoolId)
                .eq(SysRole::getType, type)
                .eq(SysRole::getDefaultRole, true);
        return this.getOne(queryWrapper);
    }
}
