package com.zsc.edu.gateway.framework.mybatisplus;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.zsc.edu.gateway.framework.security.SecurityUtil;
import com.zsc.edu.gateway.framework.security.UserDetailsImpl;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 数据权限拼装逻辑处理
 *
 */
@Slf4j
public class DataScopeHandler implements MultiDataPermissionHandler {

    /**
     * 获取数据权限 SQL 片段。
     * <p> {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆盖原有的 where 数据，如果 return 了 null 则表示不追加任何 where 条件</p>
     *
     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名
     * @param where             原有的 where 条件信息
     * @param mappedStatementId Mybatis MappedStatementId 根据该参数可以判断具体执行方法
     * @return JSqlParser       条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）
     */
    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
//        try {
            // 获取当前线程中的数据权限信息
            DataPermission dataPermission = DataPermissionContext.get();
            if (Objects.isNull(dataPermission)) {
                return null;
            }
            return buildDataScopeByAnnotation(dataPermission);

    }
    /**
     * DataScope注解方式，拼装数据权限
     * @param dataScope
     * @return
     */
    private Expression buildDataScopeByAnnotation(DataPermission dataScope) {
        UserDetailsImpl userInfo = SecurityUtil.getUserInfo();
        if (userInfo == null) {
            return null;
        }
        Set<Long> dataScopeDeptIds = userInfo.getDataScopeDeptIds();
        DataScopeType dataScopeType = userInfo.getRole().getDataScope();
        // 本人ID
        Long dataScopeCreateId = userInfo.getId();
        // 本人部门ID
        Long deptId = userInfo.getDept().getId();
        // 表别名
        String tableAlias = dataScope.tableAlias();
        // 部门限制范围的字段名称
        String deptAlias = dataScope.deptAlias();
        // 本人限制范围的字段名称
        String userAlias = dataScope.userAlias();

        // 拼装数据权限
        return switch (dataScopeType) {
            // 全部数据权限
            case DATA_SCOPE_ALL -> null;
            // 本部门数据权限, 构建部门eq表达式
            case DATA_SCOPE_DEPT -> equalsTo(tableAlias, deptAlias, deptId);
            // 本部门及以下数据权限，构建部门in表达式
            case DATA_SCOPE_DEPT_AND_CHILD -> inExpression(tableAlias, deptAlias, dataScopeDeptIds);
            // 本人数据权限，构建本人eq表达式
            case DATA_SCOPE_SELF -> equalsTo(tableAlias, userAlias, dataScopeCreateId);
        };
    }

    /**
     * 构建eq表达式
     * @param tableAlias 表别名
     * @param itemAlias 字段别名
     * @param itemId id
     * @return
     */
    private EqualsTo equalsTo(String tableAlias, String itemAlias, Long itemId) {
        if (Objects.nonNull(itemId)) {
            EqualsTo equalsTo = new EqualsTo();
            equalsTo.withLeftExpression(buildColumn(tableAlias, itemAlias));
            equalsTo.setRightExpression(new LongValue(itemId));
            return equalsTo;
        } else {
            return null;
        }
    }

    private InExpression inExpression(String tableAlias, String itemAlias, Set<Long> itemIds) {
        if (!itemIds.isEmpty()) {
            InExpression deptIdInExpression = new InExpression();
            ParenthesedExpressionList<LongValue> deptIds = new ParenthesedExpressionList<>(itemIds.stream().map(LongValue::new).collect(Collectors.toList()));
            deptIdInExpression.withLeftExpression(buildColumn(tableAlias, itemAlias));
            deptIdInExpression.setRightExpression(deptIds);
            return deptIdInExpression;
        } else {
            return null;
        }
    }


    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StringUtils.isNotEmpty(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }
        return new Column(columnName);
    }
}