package com.yeejoin.amos.boot.module.hygf.api.config;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.google.common.collect.Maps;
import com.yeejoin.amos.boot.biz.common.annotations.DataAuth;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.interceptors.PermissionInterceptorContext;
import com.yeejoin.amos.boot.biz.common.utils.CommonUtils;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.PermissionDataruleModel;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SubSelect;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

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

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class}), @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class UserEmpowerInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //获取方法注解
        Method method = getTargetDataAuthMethod(mappedStatement);
        UserEmpower userEmpower = getTargetDataAuthAnnotation(mappedStatement);
        if(userEmpower==null){
          return invocation.proceed();
        }
        //获取字段
        String[] filed= userEmpower.field();
        //获取字段条件表达式
        String[] fileCondition= userEmpower.fieldConditions();
        //获取 参数之间关系
        String fileBetweenCondition=  userEmpower.relationship();
        //获取参数值，
        List<String> orgCode = UserEmpowerThreadLocal.getDataAuthRule();
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        //获取sql
        String sql = boundSql.getSql();
        //拼接参数
        List<String> sq= selectSql(filed, fileCondition,  orgCode);
        String sqldata=" ";
        if(sq!=null&&sq.size()>0){

            if(fileCondition.length==1){

                sqldata= sqldata+sq.get(0);

            }else{
                if(fileBetweenCondition!=null&&!fileBetweenCondition.isEmpty()){

                    for (int i = 0; i < sq.size(); i++) {
                        if(i==sq.size()-1){
                            sqldata= sqldata+sq.get(i);
                        }else{
                            sqldata= sqldata+sq.get(i)+" "+fileBetweenCondition+" ";
                        }
                    }
                }else{
                    sqldata=" ";
                }
            }
        }

        Select select = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect selectBody = (PlainSelect) select.getSelectBody();
        if (!ValidationUtil.isEmpty(sqldata.trim())) {

        } else {
            sqldata=" 1= 2 ";
        }

        if (ValidationUtil.isEmpty(selectBody.getWhere())) {
            selectBody.setWhere(CCJSqlParserUtil.parseCondExpression(sqldata));
        } else {
            AndExpression andExpr = new AndExpression(selectBody.getWhere(), CCJSqlParserUtil.parseCondExpression(sqldata));
            selectBody.setWhere(andExpr);
        }
            System.out.println(selectBody.toString());
        metaObject.setValue("delegate.boundSql.sql", selectBody.toString());

        }catch (Exception e){
            e.printStackTrace();
            throw new BadRequest("权限认证失败！");
        }finally {
            UserEmpowerThreadLocal.clean();
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }


    private List<String> selectSql(String[] filed,String[] fileCondition, List<String> data){
        List<String> sql=new ArrayList<>();
        if(filed!=null&&filed.length>0&&fileCondition!=null&&fileCondition.length>0&&data!=null&&data.size()>0){

            for (int i = 0; i < filed.length; i++) {
                String sq=" ";
                sq= getCondition(filed[i],fileCondition[i],data);
                sql.add(sq);
            }
        }
        return sql;
    }

    private String getCondition(String filed,String type, List<String> data){
        String sql=" ";
        switch (type) {
            case "in":
                sql = sql+getInData( filed,data);
                break;
            case "like":
                if(data.size()==1){
                    sql = sql+getlikeData(filed,data);
                }else{

                }


                break;
            case "likeLeft":
                if(data.size()==1){
                    sql = sql+getlikeLeftData(filed,data);
                }else{

                }

                break;
            case "likeRight":

                if(data.size()==1){
                    sql = sql+getlikeRightData(filed,data);
                }else{

                }
                break;
            case "eq":

                if(data.size()==1){
                    sql = sql+getData(filed,data);
                }else{

                }
                break;
        }
        return sql;
    }
    private String getInData( String filed,List<String> data){
        String sql=" ( ";
        for (int i = 0; i < data.size(); i++) {
            if(i==data.size()-1){
                sql=sql+"'" +data.get(i)+"' ) ";
            }else{
                sql=sql+"'"+ data.get(i)+"',";
            }
        }
        return filed+" in "+sql;
    }
    private String getData(  String filed,List<String> data){
        String sql="";
        if(data.size()==1){

            sql= sql+filed+" = "+data.get(0);
        }else{
            for (int i = 0; i < data.size(); i++) {
                if(i==data.size()-1){
                    sql= sql+filed+" = "+data.get(i)+" ";
                }else{
                    sql= sql+filed+" = "+data.get(i)+"  and  ";;
                }
            }

        }

        return sql;


    }
    private String getlikeRightData(  String filed,List<String> data){
        String sql="";
        if(data.size()==1){

            sql= sql+filed+" = "+data.get(0)+ "%";
        }else {
            for (int i = 0; i < data.size(); i++) {
                if (i == data.size() - 1) {
                    sql = sql + filed + " = " + data.get(i) + "%" + " ";
                } else {
                    sql = sql + filed + " = " + data.get(i) + "%" + "  and  ";
                }
            }
        }

        return sql;
    }
    private String getlikeLeftData( String filed, List<String> data){
        String sql="";
        if(data.size()==1){

            sql= sql+filed+" = "+"%"+data.get(0);
        }else {
            for (int i = 0; i < data.size(); i++) {
                if (i == data.size() - 1) {
                    sql = sql + filed + " = " + "%"+data.get(i) +" ";
                } else {
                    sql = sql + filed + " = " + "%"+data.get(i) +"  and  ";
                }
            }
        }

        return sql;
    }
    private String getlikeData(  String filed,List<String> data){
        String sql="";
        if(data.size()==1){

            sql= sql+filed+" = "+"%"+data.get(0)+"%";
        }else {
            for (int i = 0; i < data.size(); i++) {
                if (i == data.size() - 1) {
                    sql = sql + filed + " = " + "%"+data.get(i)+"%" +" ";
                } else {
                    sql = sql + filed + " = " + "%"+data.get(i)+"%" +"  and  ";
                }
            }
        }

        return sql;
    }






    /**
     * 获取当前添加数据权限DataAuth的执行语句对应mapper方法
     *
     * @param mappedStatement
     * @return
     * @throws ClassNotFoundException
     */
    private Method getTargetDataAuthMethod(MappedStatement mappedStatement) throws ClassNotFoundException {
        String id = mappedStatement.getId();
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        final Class<?> cls = Class.forName(className);
        final Method[] methods = cls.getMethods();
        for (Method method : methods) {
            // TODO 后续重载方法需要优化
            if (method.getName().equals(methodName) && method.isAnnotationPresent(UserEmpower.class)) {
                return method;
            }
        }
        return null;
    }

    /**
     * 获取当前执行语句对应mapper方法的DataAuth注解
     *
     * @param mappedStatement
     * @return
     * @throws ClassNotFoundException
     */
    private UserEmpower getTargetDataAuthAnnotation(MappedStatement mappedStatement) throws ClassNotFoundException {
        if (ValidationUtil.isEmpty(getTargetDataAuthMethod(mappedStatement))) {
            return null;
        }
        return getTargetDataAuthMethod(mappedStatement).getAnnotation(UserEmpower.class);
    }

}