package com.leromro.framework.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.leromro.common.core.domain.ListPermission;
import com.leromro.common.core.domain.entity.SysUser;
import com.leromro.common.core.domain.model.LoginUser;
import com.leromro.common.utils.PermissionUtil;
import com.leromro.common.utils.SecurityUtils;
import com.leromro.common.utils.ServletUtils;
import com.leromro.common.utils.TableUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.parser.SimpleNode;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;


import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yanglongping
 * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的方法；
 * ParameterHandler (getParameterObject, setParameters) 拦截参数的处理；
 * ResultSetHandler (handleResultSets, handleOutputParameters) 拦截结果集的处理；
 * StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法构建的处理
 */
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Connection.class, Integer.class})})
public class ListPermissionInterceptor implements InnerInterceptor {

    private static final Map<String, String> fieldMap = MapUtil.builder(new HashMap<String, String>())
            .put("province_code","provinceCode")
            .put("city_code","cityCode")
            .put("district_code", "districtCode")
            .build();

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 1.判断是否需要修改SQL
        ListPermission permission = PermissionUtil.findPermission(parameterObject).orElse(null); //获取数据权限参数
        //如果不存在校验类，或者校验类中不存在校验字段，或权限实体为空，则跳过拦截器
        if (permission == null || StringUtils.isBlank(permission.getPermissionGroup()) || permission.getAClass() == null) return;
        //获取待校验字段
        List<String> groupList = Stream.of(StringUtils.split(permission.getPermissionGroup(), ","))
                .filter(StringUtils::isNotBlank)
                .map(String::trim)
                .collect(Collectors.toList());
        SysUser user = SecurityUtils.getLoginUser().getUser();
        //如果是超级管理员，则跳过拦截器
        if (user.getDeptId() == 100) return;
        //获取权限实体所有字段
        List<Field> fields = FieldUtils.getAllFieldsList(permission.getAClass());
        //获取表主键名称
        String tableIdName = TableUtil.getTableIdName(fields);
        //获取表名
        String tableName = TableUtil.getTableName(permission.getAClass());
        //获取权限实体所有校验字段
        List<String> fieldList = PermissionUtil.getGroupMappers(permission.getAClass());
        if (CollUtil.isEmpty(fieldList)) return;
        //获取fieldList中groupList的字段
        List<String> groupFieldList = groupList.stream()
                .filter(fieldList::contains)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(groupFieldList)) return;
        // 2.根据职责、权限实体修改SQL
        String oldSql = boundSql.getSql();
//        log.info("targetSql:{}", oldSql);
        String targetSql = "";
        try {
            Select selectStatement = (Select) CCJSqlParserUtil.parse(oldSql);
            SelectBody selectBody = selectStatement.getSelectBody();
            if (selectBody == null) {
                return;
            }
            if (selectBody instanceof PlainSelect) {
                PlainSelect plainSelect = (PlainSelect) selectBody;
                // 获取所有表及其别名
                Map<String, String> tableAliases = getAllTableAliases(plainSelect);
                //获取别名
                String alias = tableAliases.getOrDefault(tableName, null);
                //拼接where
                Expression where = plainSelect.getWhere();
                for (String column : groupFieldList) {
                    // 检查 column 是否与用户字段相同
                    String userFieldValue = getUserFieldValue(user, column);
                    if (StringUtils.isBlank(userFieldValue)) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(alias)){
                        column = alias + "." + column;
                    }
                    EqualsTo usesEqualsTo = new EqualsTo();
                    usesEqualsTo.setLeftExpression(new Column(column));
                    //当column和user字段相同时，setRightExpression为user中字段的值
                    usesEqualsTo.setRightExpression(new StringValue(userFieldValue));
                    if (where == null) {
                        where = usesEqualsTo;
                    } else {
                        where = new AndExpression(where, usesEqualsTo);
                    }
                }
                // 将新的where条件sql 设置回去
                plainSelect.setWhere(where);
                targetSql = plainSelect.toString();
//                log.info("targetSql：{}", targetSql);
            }
        } catch (JSQLParserException e) {
            e.printStackTrace();
        }

        // 3.修改完成的sql 再设置回去
        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
        mpBoundSql.sql(targetSql);
    }

    private Map<String, String> getAllTableAliases(PlainSelect plainSelect) {
        Map<String, String> tableAliases = new HashMap<>();
        Table table = plainSelect.getFromItem(Table.class);
        if (ObjectUtil.isNotNull(table.getAlias())) {
            tableAliases.put(table.getName(),table.getAlias().getName());
        }
        List<Join> joins = plainSelect.getJoins();
        if (CollUtil.isNotEmpty(joins)) {
            for (Join join : joins) {
                Table rightItem = (Table)join.getRightItem();
                if (ObjectUtil.isNotNull(rightItem.getAlias())) {
                    tableAliases.put(rightItem.getName(),rightItem.getAlias().getName());
                }
            }
        }

        return tableAliases;
    }

    private String getUserFieldValue(SysUser user, String column) {
        try {
            Field field = FieldUtils.getDeclaredField(user.getClass(), fieldMap.get(column), true);
            if (field != null) {
                field.setAccessible(true);
                Object value = field.get(user);
                if (value != null) {
                    return value.toString();
                }
            }
        } catch (Exception e) {
            log.error("Error getting user field value for column: {}", column, e);
        }
        return null;
    }


}
