package cn.cslg.pas.common.utils.auth;

import cn.cslg.pas.common.model.DataSource;
import cn.cslg.pas.common.model.PersonnelVO;
import cn.cslg.pas.common.utils.CacheUtils;
import cn.cslg.pas.common.utils.Response;
import cn.cslg.pas.common.utils.SecurityUtils.LoginUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

import static cn.cslg.pas.common.utils.JsonUtils.log;

@Order(2)
@Aspect
@Component
public class AuthAop {
    @Value("${authorUrl}")
    private String url;

    @Autowired
    private CacheUtils cacheUtils;
    @Autowired
    private LoginUtils loginUtils;

    /**
     * 定义切点
     */
    @Pointcut("@annotation(cn.cslg.pas.common.utils.auth.checkAuth)")
    public void annotationPointcut() {

    }

    /**
     * @param joinPoint 当前执行的方法
     */
    @Around("annotationPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            //获得登录人信息
            PersonnelVO personnelVO = cacheUtils.getLoginUserPersonnel(loginUtils.getId());
            if (personnelVO.getState() == 0) {
                return Response.error("登录账号已被禁用，请联系管理员启用");
            }


            // 是否通过切面过滤标记
            Boolean isPass = true;
            MethodSignature ms = (MethodSignature) joinPoint.getSignature();
            //获得执行方法对象
            Method method = ms.getMethod();
            //获得执行方法对象上的@checkAuth，@Operation注解对象
            checkAuth myAnnotation = method.getAnnotation(checkAuth.class);
            Operation operAnnotation = method.getAnnotation(Operation.class);
            //获得@checkAuth注解上FunId参数的值
            String functionId = myAnnotation.FunId();
            //获得执行方法的参数对象
            Object[] args = joinPoint.getArgs();
            //根据登录人的id以及功能id获得规则信息
            //将登录人的id以及功能id放入requestBody中
            RequestBody requestBody = new FormBody.Builder()
                    .add("loginId", loginUtils.getId().toString())
                    .add("functionId", functionId)
                    .build();
            //建立远程连接
            OkHttpClient okHttpClient = new OkHttpClient();
            //发送请求
            Request request = new Request.Builder()
                    .url(url + "/permission/api/data/queryDataRule")
                    .addHeader("Cookie", LoginUtils.getToken())
                    .post(requestBody)
                    .build();
            //获得请求结果
            String resBody = Objects.requireNonNull(okHttpClient.newCall(request).execute().body()).string();
            JSONArray jsonArray = JSONArray.parseArray(resBody);
            //如果获得规则的返回值为[-1]则代表登录人没有使用该功能的权限
            if (jsonArray.get(0).toString().equals("-1")) {
                return Response.error("没有" + operAnnotation.summary() + "的功能");
            }
            //如果获得规则的返回值为[0],则直接通过判断
            else if (jsonArray.size() == 1 && jsonArray.get(0).equals("0")) {
                return joinPoint.proceed();
            }
            // 如果查询结果的size大于0证明有限制逻辑
            if (jsonArray.size() > 0) {
                RequestBody reBodySource = new FormBody.Builder()
                        .add("tableName", "local")
                        .build();
                //处理jsonObject,变为（x==y）&&（z==t）的形式 ,并用js引擎进行boolean判断
                //建立连接去获得字典信息
                OkHttpClient okHttpClientSou = new OkHttpClient();
                //发送请求
                Request requestSou = new Request.Builder()
                        .url(url + "/permission/api/data/getDataSource")
                        .post(reBodySource)
                        .addHeader("Cookie", LoginUtils.getToken())
                        .build();
                //获得请求返回
                String resSource = Objects.requireNonNull(okHttpClientSou.newCall(requestSou).execute().body()).string();

                JSONArray jsonArray1 = JSON.parseArray(resSource);
                // 获得字典
                List<DataSource> dataSources = jsonArray1.toJavaList(DataSource.class);
                //循环遍历将多个规则拼接起来
                StringBuilder sqlStr = new StringBuilder();
                for (int i = 0; i < jsonArray.size(); i++) {
                    //将数据库里存的规则转换为可识别的判断逻辑
                    String sql = TreeUtils.reCompute(JSONObject.parseObject(jsonArray.get(i).toString()), args, dataSources, personnelVO);
                    sqlStr.append(jsonArray.size() != i + 1 ? sql + " || " : sql);
                }
                //js引擎进行判断
                ScriptEngineManager manager = new ScriptEngineManager();
                //根据名字获得引擎
                ScriptEngine engine = manager.getEngineByName("javascript");
                //进行判断,生成判断结果并将判断结果赋给isPass
                Object result = engine.eval(sqlStr.toString());//进行判断
                isPass = (Boolean) result;
            }

            //判断不通过
            if (!isPass) {
                return Response.error("没有权限进行" + operAnnotation.summary() + "的操作");
            }
            //判断通过
            return joinPoint.proceed();
        }
        catch (Exception var10) {
            log.error("operlog exception:{}", var10);
            return joinPoint.proceed();
        }

    }


    @Before(value = "annotationPointcut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        this.handleAfterLog(joinPoint);

    }

    protected void handleAfterLog(JoinPoint joinPoint) {
        try {
            log.info("after ----> title:{}, desc:{}",1,2);
            return;
        } catch (Exception var10) {
            log.error("operlog exception:{}", var10);
            return;
        }
    }

}

