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


import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.module.hygf.api.entity.StdUserEmpower;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.commons.lang.ArrayUtils;
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.*;
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.springframework.beans.factory.annotation.Autowired;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.*;

@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 {



    @Autowired
    private RedisUtils redisUtils;




    @Override
    public Object intercept(Invocation invocation) throws Throwable {



            try {
                if (null == redisUtils ){
                    return invocation.proceed();
                }
                StdUserEmpower orgCode =(StdUserEmpower) redisUtils.get("Emp_"+RedisKey.buildReginKey(RequestContext.getExeUserId(), RequestContext.getToken()));



                if(orgCode!=null&& orgCode.isFlag()&&!orgCode.isFarmer()){
                    StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
                    MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
                    MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
                    //获取方法注解
              //      Method method = getTargetDataAuthMethod(mappedStatement);
                    UserEmpower userEmpower;
                    if (mappedStatement.getId().contains("_COUNT")){
                        String id  = mappedStatement.getId().replace("_COUNT", "");
                        userEmpower = getTargetDataAuthAnnotationCount(id);
                        if (userEmpower == null) {
                            return invocation.proceed();
                        }
                    }else {
                        userEmpower = getTargetDataAuthAnnotation(mappedStatement);
                    }

                    if(userEmpower==null){
                        return invocation.proceed();
                    }
                    //StdUserEmpower orgCode = UserEmpowerThreadLocal.getDataAuthRule();


                    if(orgCode!=null&&orgCode.isFlag()){
                        //获取字段
                        String[] filed= userEmpower.field();
                        //获取字段条件表达式
                        String[] fileCondition= userEmpower.fieldConditions();
                        //获取 参数之间关系
                        String[] fileBetweenCondition=  userEmpower.relationship();
                        //获取  是否特殊
                        Boolean specific= userEmpower.specific();


                        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
                        //获取sql
                        String sql = boundSql.getSql();
                        //拼接参数
                        List<String> sq= selectSql(filed, fileCondition,  orgCode,specific);
                        String sqldata=" ";
                        if(sq!=null&&sq.size()>0){

                            if(fileCondition.length==1){

                                sqldata= sqldata+sq.get(0);

                            }else{
                                if(fileBetweenCondition!=null&&fileBetweenCondition.length>0){

                                    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[i]+" ";
                                        }
                                    }
                                }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());
                    }else{
                        return invocation.proceed();
                    }
                }else if(orgCode!=null&& !orgCode.isFlag()&&!orgCode.isFarmer()){
                   //经销商
                    StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
                    MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
                    MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
                    //获取方法注解
                    BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
                    String sql = boundSql.getSql();
                    UserEmpower userEmpower ;
                    if (mappedStatement.getId().contains("_COUNT")){
                            String id  = mappedStatement.getId().replace("_COUNT", "");
                            userEmpower = getTargetDataAuthAnnotationCount(id);
                            if (userEmpower == null) {
                                return invocation.proceed();
                            }
                    }else {
                         userEmpower = getTargetDataAuthAnnotation(mappedStatement);
                        if (userEmpower == null) {
                            return invocation.proceed();
                        }
                    }

                    //获取字段
                    String[] filed = userEmpower.dealerField();

                    if (filed.length < 1){
                        return invocation.proceed();
                    }

                    //获取字段条件表达式
                    String[] fileCondition = userEmpower.fieldConditions();
                    //获取  是否特殊
                    Boolean specific= userEmpower.specific();
                    //获取参数值，
                    StdUserEmpower dataAuthRule =(StdUserEmpower) redisUtils.get("Emp_"+RedisKey.buildReginKey(RequestContext.getExeUserId(), RequestContext.getToken()));
                    String[] data = new String[]{String.join(",",dataAuthRule.getAmosOrgCode()),dataAuthRule.getAdminRegionalCompaniesCode() == null? dataAuthRule.getRegionalCompaniesCode():dataAuthRule.getAdminRegionalCompaniesCode(),dataAuthRule.getUserId(),dataAuthRule.getRegionalCompaniesCode()};
                    List<String> list = new ArrayList<>(Arrays.asList(data));
                    list.removeAll(Collections.singleton(null));

                    List<String> sq ;
                    //获取sql
                    List<String> fileds = Arrays.asList(filed);
                    if (!ValidationUtil.isEmpty(dataAuthRule.getAdminRegionalCompaniesCode())  && fileds.size() >1) {
                        sq = selectSqlJXS(new String[]{fileds.get(0), fileds.get(1)}, fileCondition, list);
                    } else if (!ValidationUtil.isEmpty(dataAuthRule.getUserId())) {
                        sq = selectSqlJXS(new String[]{fileds.get(0),fileds.get(1),fileds.size()<3?fileds.get(0):fileds.get(2)},new String[]{fileCondition[0],fileCondition[1],fileCondition[2]} , list);
                    } else {
                        sq = selectSqlJXS(new String[]{fileds.get(0)}, fileCondition, specific?list:Arrays.asList(list.get(1)));
                    }

                    String sqldata = " ";
                    if (sq != null && sq.size() > 0) {

                        if (fileCondition.length == 1) {

                            sqldata = sqldata + sq.get(0);

                        } else {

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

                        }
                    }

                    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 {
            }
            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, StdUserEmpower stdUserEmpower,boolean specific){
        List<String> sql=new ArrayList<>();

      if(specific){
          List<String> data= stdUserEmpower.getAmosOrgCode();
          List<String> notdata= stdUserEmpower.getEliminateAmosOrgCode();
          List<String> wnotdata= stdUserEmpower.getNOeliminateAmosOrgCode();
          if( filed!=null&&filed.length>0){

              for (int i = 0; i < filed.length; i++) {
                  String sq=" ";
                  sq= getConditiontd(filed[i],fileCondition[i],data,notdata,wnotdata);
                  sql.add(sq);
              }
          }
      }else{
          List<String> data= stdUserEmpower.getAmosOrgCode();
          List<String> notdata= stdUserEmpower.getEliminateAmosOrgCode();
          List<String> wnotdata= stdUserEmpower.getNOeliminateAmosOrgCode();

          if( filed!=null&&filed.length>0){

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



        return sql;
    }

    //特定管理端条件
    private String getConditiontd(String filed,String type, List<String> data, List<String> wdata, List<String> wnotdata){
        String sql=" ";

        if(data==null||data.size()==0){

            //内部不选   外部不选
            if(wdata==null||wdata.size()==0){
                //返回空不处理
                sql=" ";
            }

            //内部不选    外部全部
            //内部不选    外部选一部分
            if(wdata!=null&&wdata.size()>0){

                sql = sql+getInData( filed,wdata);
            }

        }else if(data.contains("all")){

            //内部全选， 外部不选
            if(wdata==null||wdata.size()==0){
                sql = sql+ getnotInData( filed,wnotdata);
            }
            //内部全选， 外度全选
            if(wdata!=null&&wdata.size()!=0&&wnotdata==null){
                //返回恒等看所有
                sql= sql+" 1=1 ";
            }
            //内部全选， 外部选一部分
            if(wdata!=null&&wdata.size()!=0&&wnotdata!=null){
                //返回恒等看所有
                sql = sql+getnotInData( filed,wnotdata);
            }
        }else{
            //内部选一部分， 外度全选
            if(wdata!=null&&wdata.size()!=0&&wnotdata==null){

                sql = sql+getInData( filed,data);
            }
            //内部选一部分   外部选一部分
            if(wdata!=null&&wdata.size()!=0&&wnotdata!=null){

                data.addAll(wdata);
                sql = sql+getInData( filed,data);
            }
            //内部选一部分    外部不选
            if(wdata==null||wdata.size()==0){
                sql = sql+getInData( filed,data);
            }
        }
        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{
                    sql = sql+getData(filed,data);
                }
                break;
        }
        return sql;
    }

    private String getConditionJXS(String filed,String type, List<String> data){
        String sql=" ";
        switch (type) {
            case "in":
                sql = sql+getInDataJXS( 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{
                    sql = sql+getData(filed,data);
                }
                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 getInDataJXS( String filed,List<String> data){

        List<String> list = Arrays.asList(data.get(0).split(","));
        String sql=" ( ";
        for (int i = 0; i < list.size(); i++) {
            if(i==list.size()-1){
                sql=sql+"'" +list.get(i)+"' ) ";
            }else{
                sql=sql+"'"+ list.get(i)+"',";
            }
        }
        return filed+" in "+sql;
    }


    private String getnotInData( 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+" not 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;
    }






    /**
     * 获取当前添加数据权限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;
    }

    private Method getTargetDataAuthMethodCount(String id) throws ClassNotFoundException {
        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);
    }

    private List<String> selectSqlJXS(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 = getConditionJXS(filed[i], fileCondition[i], Arrays.asList(data.get(i)));
                sql.add(sq);
            }
        }
        return sql;
    }

    private UserEmpower getTargetDataAuthAnnotationCount(String id) throws ClassNotFoundException {

        if (ValidationUtil.isEmpty(getTargetDataAuthMethodCount(id))) {
            return  null;
        }
        return getTargetDataAuthMethodCount(id).getAnnotation(UserEmpower.class);
    }




    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;
    }

    private boolean isCountQuery(String sql) {
        // 判断 SQL 是否为 COUNT 查询
        boolean flag = sql.startsWith("SELECT count(");
        return flag;
    }


}