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

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.qmth.teachcloud.common.bean.dto.DataPermissionInfo;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicRoleDataPermission;
import com.qmth.teachcloud.common.entity.SysPrivilege;
import com.qmth.teachcloud.common.entity.SysRole;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.DataPermissionTypeEnum;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.PrivilegeEnum;
import com.qmth.teachcloud.common.mapper.BasicRoleDataPermissionMapper;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description: 数据权限服务实现类
 * @Author: CaoZixuan
 * @Date: 2022-12-06
 */
@Service
public class BasicRoleDataPermissionServiceImpl extends ServiceImpl<BasicRoleDataPermissionMapper, BasicRoleDataPermission> implements BasicRoleDataPermissionService {

    @Resource
    SysPrivilegeService sysPrivilegeService;

    @Resource
    SysRoleService sysRoleService;

    @Resource
    SysUserService sysUserService;

    @Resource
    SysOrgService sysOrgService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveRoleDataPermission(Long roleId, List<DataPermissionInfo> dataPermissionInfoList) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        Object schoolIdObj = ServletUtil.getRequestHeaderSchoolIdByNotVaild();
        Long schoolId = schoolIdObj == null ? null : SystemConstant.convertIdToLong(String.valueOf(ServletUtil.getRequestHeaderSchoolIdByNotVaild()));

        List<Long> privilegeIdList = dataPermissionInfoList.stream().map(DataPermissionInfo::getPrivilegeId).distinct()
                .collect(Collectors.toList());

        if (privilegeIdList.size() > 0) {
            int count = sysPrivilegeService.count(
                    new QueryWrapper<SysPrivilege>().lambda().ne(SysPrivilege::getType, PrivilegeEnum.MENU).in(SysPrivilege::getId, privilegeIdList));

            if (count > 0) {
                throw ExceptionResultEnum.ERROR.exception("角色数据权限要绑在菜单级权限上");
            }
            QueryWrapper<BasicRoleDataPermission> basicRoleDataPermissionQueryWrapper = new QueryWrapper<>();
            if (Objects.nonNull(schoolId)) {
                basicRoleDataPermissionQueryWrapper.lambda().eq(BasicRoleDataPermission::getSchoolId, schoolId);
            }
            basicRoleDataPermissionQueryWrapper.lambda().eq(BasicRoleDataPermission::getRoleId, roleId);
            this.remove(basicRoleDataPermissionQueryWrapper);

            List<BasicRoleDataPermission> basicRoleDataPermissionList = dataPermissionInfoList.stream()
                    .filter(e -> Objects.nonNull(e.getDataPermissionType())).flatMap(e -> {
                        BasicRoleDataPermission basicRoleDataPermission = new BasicRoleDataPermission();
                        basicRoleDataPermission.insertInfo(requestUser.getId());
                        basicRoleDataPermission.setSchoolId(schoolId);
                        basicRoleDataPermission.setRoleId(roleId);
                        basicRoleDataPermission.setPrivilegeId(e.getPrivilegeId());
                        basicRoleDataPermission.setDataPermissionType(e.getDataPermissionType());
                        return Stream.of(basicRoleDataPermission);
                    }).collect(Collectors.toList());
            this.saveBatch(basicRoleDataPermissionList);
        }
    }

    @Override
    public List<DataPermissionInfo> findRoleDataPermissionInfo(Long roleId) {
        return this.list(
                        new QueryWrapper<BasicRoleDataPermission>().lambda().eq(BasicRoleDataPermission::getRoleId, roleId))
                .stream().flatMap(e -> {
                    DataPermissionInfo dataPermissionInfo = new DataPermissionInfo();
                    dataPermissionInfo.setPrivilegeId(e.getPrivilegeId());
                    dataPermissionInfo.setDataPermissionType(e.getDataPermissionType());
                    return Stream.of(dataPermissionInfo);
                }).distinct().collect(Collectors.toList());
    }

    @Override
    public DataPermissionRule findDataPermission(Long schoolId, Long requestUserId, String url) {

        // 方法的权限
        SysPrivilege sysPrivilege = new SysPrivilege();
        List<SysPrivilege> sysPrivilegeList = sysPrivilegeService.list(
                new QueryWrapper<SysPrivilege>().lambda().eq(SysPrivilege::getUrl, url).eq(SysPrivilege::getType, PrivilegeEnum.URL));
        // url权限默认取第一条
        if (sysPrivilegeList.size() > 0) {
            sysPrivilege = sysPrivilegeList.get(0);
        }

        return analysisDataPermission(sysPrivilege, requestUserId, schoolId);

    }

    @Override
    public DataPermissionRule findDataPermission() {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        return this.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
    }

    @Override
    public DataPermissionRule findDataPermission(Long privilegeId) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        if(privilegeId == null){
            DataPermissionRule dataPermissionRule = new DataPermissionRule();
            dataPermissionRule.setRequestUserId(requestUser.getId());
            return dataPermissionRule;
        }
        SysPrivilege sysPrivilege = sysPrivilegeService.getById(privilegeId);
        return this.analysisDataPermission(sysPrivilege, requestUser.getId(), requestUser.getSchoolId());
    }

    @Override
    public DataPermissionRule findDataPermission(Long privilegeId, SysUser user) {
        SysPrivilege sysPrivilege = sysPrivilegeService.getById(privilegeId);
        return this.analysisDataPermission(sysPrivilege, user.getId(), user.getSchoolId());
    }

    /**
     * 计算多角色中的最大权限并解析
     *
     * @param sysPrivilege  权限菜单对象
     * @param requestUserId 用户ID
     */
    private DataPermissionRule analysisDataPermission(SysPrivilege sysPrivilege, Long requestUserId, Long schoolId) {
        DataPermissionRule result = new DataPermissionRule();
        if (Objects.nonNull(sysPrivilege)) {
            // 角色集合
            List<SysRole> sysRoleList = sysRoleService.listRolesByUserId(requestUserId);
            // 数据库中存在该方法的url
            int maxWeight = 0;
            DataPermissionTypeEnum finalPermissionType = null;
            for (SysRole sysRole : sysRoleList) {
                DataPermissionTypeEnum roleDataPermissionType = this.searchRoleDataPermissionByPrivilege(sysRole, sysPrivilege, schoolId);
                if (Objects.nonNull(roleDataPermissionType)) {
                    // 角色数据权限不为空时，筛选出所有角色在该url中的最大范围数据权限为该url的最终数据权限
                    int weight = roleDataPermissionType.getWeight();
                    if (weight > maxWeight) {
                        // 更新为当前找到的最高权限类型
                        maxWeight = weight;
                        finalPermissionType = roleDataPermissionType;
                    }
                }
            }

            // 默认为本人数据权限
            if (Objects.isNull(finalPermissionType)) {
                finalPermissionType = DataPermissionTypeEnum.SELF;
            }
            // 最终数据权限不为空
            SysUser requestUser = sysUserService.getById(requestUserId);
            switch (finalPermissionType) {
                case SELF:
                    // 只能看自己创建的
                    result.setRequestUserId(requestUserId);
                    break;
                case SELF_COURSE:
                    result.setCourseUserId(requestUserId);
                    break;
                case SELF_ORG:
                    // 只能看自己所在机构
                    Long orgId = requestUser.getOrgId();
                    result.setOrgIdSet(new HashSet<>(Arrays.asList(orgId)));
                    break;
                case SELF_ORG_BELOW:
                    // 只能看自己所在机构和所辖机构
                    result.setOrgIdSet(sysOrgService.findDeepOrgIdListByUserId(requestUser.getId()));
                    break;
                default:
                    break;
            }
        }
        return result;
    }

    /**
     * 递归搜索角色的数据权限
     *
     * @param sysRole   角色对象
     * @param privilege url权限
     * @return 角色数据权限规则
     */
    private DataPermissionTypeEnum searchRoleDataPermissionByPrivilege(SysRole sysRole, SysPrivilege privilege, Long schoolId) {
        Long privilegeId = privilege.getId();
        PrivilegeEnum type = privilege.getType();
        // 如果是菜单，搜索该菜单是否绑定了数据权限
        if (PrivilegeEnum.MENU.equals(type)) {
            QueryWrapper<BasicRoleDataPermission> queryWrapper = new QueryWrapper<>();
            LambdaQueryWrapper<BasicRoleDataPermission> lambda = queryWrapper.lambda();
            lambda.eq(BasicRoleDataPermission::getRoleId, sysRole.getId())
                    .eq(BasicRoleDataPermission::getPrivilegeId, privilegeId);
            if (sysRole.getDefaultRole()) {
                lambda.isNull(BasicRoleDataPermission::getSchoolId);
            } else {
                lambda.eq(BasicRoleDataPermission::getSchoolId, schoolId);
            }
            BasicRoleDataPermission basicRoleDataPermission = this.getOne(queryWrapper);
            if (Objects.nonNull(basicRoleDataPermission)) {
                // 有数据权限
                return basicRoleDataPermission.getDataPermissionType();
            }
        }
        // 如果该url不是菜单类型则查询其父目录
        return getDataPermissionRule(sysRole, privilege, schoolId);
    }

    /**
     * 查询父菜单是否包含数据权限
     *
     * @param sysRole   角色对象
     * @param privilege 权限
     * @return 数据权限规则
     */
    private DataPermissionTypeEnum getDataPermissionRule(SysRole sysRole, SysPrivilege privilege, Long schoolId) {
        Long parentId = privilege.getParentId();
        if (SystemConstant.longNotNull(parentId)) {
            SysPrivilege parent = sysPrivilegeService.getById(parentId);
            if (Objects.nonNull(parent)) {
                return this.searchRoleDataPermissionByPrivilege(sysRole, parent, schoolId);
            }
        }
        return DataPermissionTypeEnum.SELF;
    }
}
