package cn.cslg.report.common.config.InnerInterceptor;

import cn.cslg.report.common.model.DataSource;
import cn.cslg.report.common.model.vo.PersonnelVO;
import cn.cslg.report.common.utils.CacheUtils;
import cn.cslg.report.common.utils.SecurityUtils.LoginUtils;
import cn.cslg.report.common.utils.SecurityUtils.SecurityUtils;
import cn.cslg.report.common.utils.auth.TreeUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
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.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

@Slf4j
public class LizzMybatisIntercepts implements InnerInterceptor {
    @Lazy//懒加载，当调用时注入
    @Autowired
    private CacheUtils cacheUtils;
    @Lazy//懒加载，当调用时注入
    @Autowired
    private LoginUtils loginUtils;

    @Value("${PCSUrl}")
    private String url;

    @SneakyThrows
    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        try {
            //获得原始sql
            String Localsql = boundSql.getSql();
            Map<String, Object> maps = SecurityUtils.getDataScope(); //获得线程里保存的functionId
            //如果线程里有保存信息则进行sql拦截操作，若没有则sql保持不变
            if (maps != null) {
                SecurityUtils.cleanDataScope(); //当第一次进来后删除线程保存的functionId,避免后续使用的查询sql进来
                String token = maps.get("token").toString();
                //查询数据规则
                //设定formdata类型参数
                RequestBody requestBody = new FormBody.Builder()
                        .add("loginId", maps.get("loginId").toString())
                        .add("functionId", maps.get("functionId").toString())
                        .build();
                //建立连接
                OkHttpClient okHttpClient = new OkHttpClient();
                //发送远程请求
                Request request = new Request.Builder()
                        .url(url + "/permission/api/data/queryDataRule")
                        .post(requestBody)
                        .addHeader("Cookie", token)
                        .build();
                //获得请求返回
                String resBody = Objects.requireNonNull(okHttpClient.newCall(request).execute().body()).string();
                //处理请求返回
                JSONArray jsonArray = JSONArray.parseArray(resBody);
                //当返回结果不为[0]或[-1]进行sql拦截操作
                if (!(jsonArray.get(0).equals("0") || jsonArray.get(0).equals("-1"))) {
                    StringBuilder sqls = new StringBuilder();
                    // 查询字典数据
                    //设定formdata类型参数
                    RequestBody reBodySource = new FormBody.Builder()
                            .add("tableName", "local")
                            .build();
                    //建立连接
                    OkHttpClient okHttpClientSou = new OkHttpClient();
                    //发送请求
                    Request requestSou = new Request.Builder()
                            .url(url + "/permission/api/data/getDataSource")
                            .post(reBodySource)
                            .addHeader("Cookie", token)
                            .build();
                    //获得请求返回
                    String resSource = Objects.requireNonNull(okHttpClientSou.newCall(requestSou).execute().body()).string();

                    JSONArray jsonArray1 = JSON.parseArray(resSource);
                    // 获得字典
                    List<DataSource> dataSources = jsonArray1.toJavaList(DataSource.class);
                    //拼接所有角色的限制条件
                    if (jsonArray.size() > 0) {
                        PersonnelVO personnelVO = cacheUtils.getLoginUserPersonnel(loginUtils.getId());
                        for (int i = 0; i < jsonArray.size(); i++) {
                            String sql1 = TreeUtils.reSql(JSONObject.parseObject(jsonArray.get(i).toString()), dataSources, personnelVO);
                            if (!sql1.equals("")) {
                                sqls.append(jsonArray.size() != i + 1 ? sql1 + " OR " : sql1);
                            }
                        }
                        // 根据sql语句结构，将拼接sql放入合适的位置
                        if (Localsql.contains("order by")) {
                            Localsql = Localsql.replace("order by", "And (" + sqls + ") order by");
                        } else {
                            Localsql += " And (" + sqls + ")";
                        }
                        //将限制条件拼接到sql语句

                        Field field = boundSql.getClass().getDeclaredField("sql");//反射获得boundsql的sql属性并改变sql参数
                        field.setAccessible(true); //属性设为可见（反射不安全的原因）
                        field.set(boundSql, Localsql);
                    }
                }
            }

            return true;
        } finally {
            SecurityUtils.cleanDataScope();//无论是否进行成功最后都要删除线程里的保存的参数

        }
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

    }

    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        return true;
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
    }

    @Override
    public void beforeGetBoundSql(StatementHandler sh) {
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
