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

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.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.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
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<BasicRoleDataPermission>();
            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);

    }

    @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();
        SysPrivilege sysPrivilege = sysPrivilegeService.getById(privilegeId);
        return this.analysisDataPermission(sysPrivilege, requestUser.getId());
    }

    /**
     * 计算多角色中的最大权限并解析
     *
     * @param sysPrivilege  权限菜单对象
     * @param requestUserId 用户ID
     * @return
     */
    private DataPermissionRule analysisDataPermission(SysPrivilege sysPrivilege, Long requestUserId) {
        DataPermissionRule result = new DataPermissionRule();
        if (Objects.nonNull(sysPrivilege)) {
            // 角色集合
            List<Long> roleIdList = sysRoleService.getUserRoles(requestUserId);
            // 数据库中存在该方法的url
            int maxWeight = 0;
            for (Long roleId : roleIdList) {
                DataPermissionTypeEnum roleDataPermissionType = this.searchRoleDataPermissionByPrivilege(roleId, sysPrivilege);
                if (Objects.nonNull(roleDataPermissionType)) {
                    // 角色数据权限不为空时，筛选出所有角色在该url中的最大范围数据权限为该url的最终数据权限
                    int weight = roleDataPermissionType.getWeight();
                    if (weight > maxWeight) {
                        maxWeight = weight;
                    }
                }
            }
            DataPermissionTypeEnum finalPermissionType = DataPermissionTypeEnum.findByWeight(maxWeight);
            if (Objects.nonNull(finalPermissionType)) {
                // 最终数据权限不为空
                SysUser requestUser = sysUserService.getById(requestUserId);
                switch (finalPermissionType) {
                    case SELF:
                        // 只能看自己创建的
                        result.setRequestUserId(requestUserId);
                        break;
                    case SELF_ORG:
                        // 只能看自己所在机构
                        Long orgId = requestUser.getOrgId();
                        Set<Long> orgIdSet = new HashSet<>();
                        orgIdSet.add(orgId);
                        result.setOrgIdSet(orgIdSet);
                        break;
                    case SELF_ORG_BELOW:
                        // 只能看自己所在机构和所辖机构
                        result.setOrgIdSet(sysOrgService.findDeepOrgIdListByUserId(requestUser.getId()));
                        break;
                    case ALL:
                        break;
                }
            }
        }
        return result;
    }

    ;

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

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