package com.chushang.datascope.aspect;

import cn.hutool.core.collection.CollectionUtil;
import com.chushang.common.core.exception.ResultException;
import com.chushang.common.core.text.Convert;
import com.chushang.common.mybatis.page.CommonParam;
import com.chushang.common.core.util.StringUtils;
import com.chushang.common.mybatis.base.BaseEntity;
import com.chushang.datascope.annotation.DataScope;
import com.chushang.datascope.constants.ScopeConstants;
import com.chushang.datascope.entity.DataScopeEntity;
import com.chushang.datascope.enums.ScopeKeyWord;
import com.chushang.security.context.SecurityContextHolder;
import com.chushang.security.utils.SecurityUtils;
import com.chushang.security.entity.vo.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

@Aspect
@Slf4j
@Component
public class DataScopeAspect {
    @Pointcut("@annotation(com.chushang.datascope.annotation.DataScope)")
    public void dataScopePointCut() {
    }

    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable {
        if (log.isDebugEnabled()){
            log.debug("数据权限before 进入");
        }
        clearDataScope(point);
        handleDataScope(point);
    }

    protected void handleDataScope(final JoinPoint joinPoint) {
        // 获取当前的用户
        LoginUser<?,DataScopeEntity> loginUser = SecurityUtils.getLoginUser();
        if (StringUtils.isNotNull(loginUser)) {
            List<DataScopeEntity> scopes = loginUser.getScopes();
            Long userId = loginUser.getUserId();
            // 如果是超级管理员，则不过滤数据
            if (!SecurityUtils.isAdmin())
            {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Method method = signature.getMethod();
                DataScope dataScope =
                        method.getAnnotation(DataScope.class);
                String permission = StringUtils.defaultIfEmpty(dataScope.permission(),
                        SecurityContextHolder.getPermission());
                ScopeKeyWord scopeKeyWord = dataScope.scopeKey();
                dataScopeFilter(joinPoint, scopes, dataScope.deptAlias(),
                        dataScope.userAlias(), permission, userId, scopeKeyWord);
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint  切点
     * @param dataScopes 用户角色数据权限
     * @param deptAlias  部门别名
     * @param userAlias  用户别名
     * @param userId 用户id
     */
    public void dataScopeFilter(JoinPoint joinPoint, List<DataScopeEntity> dataScopes, String deptAlias, String userAlias, String permission, Long userId, ScopeKeyWord scopeKeyWord) {
        StringBuilder dataScopeSqlString = new StringBuilder();
        // 根据部门过滤role 显示
        StringBuilder roleSqlString = new StringBuilder();
        List<String> conditions = new ArrayList<>();
        for (DataScopeEntity scope : dataScopes) {
            String dataScope = scope.getScope();
            Set<String> permissions = scope.getPermissions();
            // 自定义
            if (!ScopeConstants.DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) {
                continue;
            }
            if (StringUtils.isNotEmpty(permission) && CollectionUtil.isNotEmpty(permissions)
                    && !CollectionUtil.containsAny(permissions, Convert.toList(permission))) {
                continue;
            }
            // 全部
            if (ScopeConstants.DATA_SCOPE_ALL.equals(dataScope)) {
                dataScopeSqlString = new StringBuilder();
                break;
            }
            // 自定义
            else if (ScopeConstants.DATA_SCOPE_CUSTOM.equals(dataScope)) {
                // 这个时候就会报错
                dataScopeSqlString
                        .append(ScopeKeyWord.OR.getCode())
                        .append(StringUtils.format(
                        "{}.dept_id IN ({}) ", deptAlias, String.join(",",scope.getDeptIds())));
            }
            // 部门数据
            else if (ScopeConstants.DATA_SCOPE_DEPT.equals(dataScope)) {
                dataScopeSqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, scope.getDeptId()));
            }
            // 部门及以下
            else if (ScopeConstants.DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                dataScopeSqlString
                        .append(ScopeKeyWord.OR.getCode())
                        .append(StringUtils.format(
                        "{}.dept_id IN ({})",
                        deptAlias, String.join(",",scope.getDeptIds())));
                // 当且仅当用在角色列表页面, 并且角色为 部门及以下 -- 用于控制 角色的显示, 每个部门下有自己的角色不同
                roleSqlString
                        .append(ScopeKeyWord.OR.getCode())
                        .append(StringUtils.format("{}.dept_id IN ({}) ",
                        "r", String.join(",",scope.getDeptIds())));
            }
            // 仅本人
            else if (ScopeConstants.DATA_SCOPE_SELF.equals(dataScope)) {
                if (StringUtils.isNotBlank(userAlias)) {
                    dataScopeSqlString
                            .append(ScopeKeyWord.OR.getCode())
                            .append(StringUtils.format("{}.user_id = {} ", userAlias, userId));
                } else {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    dataScopeSqlString
                            .append(ScopeKeyWord.OR.getCode())
                            .append(StringUtils.format("{}.dept_id = {} ", deptAlias, scope.getDeptId()));
                }
            }
            conditions.add(dataScope);
        }
        if (StringUtils.isNotBlank(dataScopeSqlString.toString())) {
            String v;
            if (scopeKeyWord.equals(ScopeKeyWord.AND)){
                v = ScopeKeyWord.AND.getCode();
            }else {
                v = ScopeKeyWord.OR.getCode();
            }
            // 会将 最前面的 OR 去除
            v += "(" + dataScopeSqlString.substring(4) + ")";
            Map<String, Object> sqlParam = getSqlParam(joinPoint);
            if (null == sqlParam) return;
            sqlParam.put(ScopeConstants.DATA_SCOPE, v);
            sqlParam.put(ScopeConstants.ROLE_SCOPE, roleSqlString.toString());
        }
    }

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        Map<String, Object> sqlParam = getSqlParam(joinPoint);
        if (null == sqlParam) return;
        sqlParam.put(ScopeConstants.DATA_SCOPE, "");
        sqlParam.put(ScopeConstants.ROLE_SCOPE, "");
    }

    private Map<String, Object> getSqlParam(final JoinPoint joinPoint){
        Object params = joinPoint.getArgs()[0];
        Map<String, Object> sqlParam;
        if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) params;
            sqlParam = baseEntity.getSqlParam();
        } else if (StringUtils.isNotNull(params) && params instanceof CommonParam) {
            CommonParam commonParam = (CommonParam) params;
            sqlParam = commonParam.getSqlParam();
        }else {
            return null;
        }
        return sqlParam;
    }
}
