package org.matrix.actuators.checkpoint;

import cn.hutool.script.ScriptUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.PathNotFoundException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.matrix.actuators.Actuator;
import org.matrix.actuators.httpclient.HttpResponseDetail;
import org.matrix.actuators.util.CompleteExpressionUtil;
import org.matrix.enums.ExecutionHistoryStatus;
import org.matrix.exception.CheckPointException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


/**
 * 数据检测点工具
 * todo 打印LOG
 *
 * @author huangxiahao
 */
@Component
public class CheckPointActuator implements Actuator {

    String baseJsString;

    final
    CompleteExpressionUtil completeExpressionUtil ;

    public CheckPointActuator(@Value("${baseJsPath}") String baseJs, CompleteExpressionUtil completeExpressionUtil) {
        this.completeExpressionUtil = completeExpressionUtil;
        ClassPathResource cpr = new ClassPathResource(baseJs);
        try {
            this.baseJsString = IOUtils.toString(cpr.getInputStream(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new CheckPointException("初始JS加载失败");
        }
    }


    public CheckPointResult httpCheck(HttpResponseDetail httpResponseDetail, CheckPoint checkPoint,Long envId,Long projectId) {
        CheckPointResult checkPointResult = new CheckPointResult();
        //根据checkPoint里的细节数据开始检测
        //异常检查点检测
        if (checkPoint.getUseExceptionCheck()) {
            checkPointResult.addCheckPointResultDetail(exceptionCheck(httpResponseDetail));
        }
        //异常检查点检测
        if (checkPoint.getUseNullCheck()) {
            checkPointResult.addCheckPointResultDetail(nullCheck(httpResponseDetail.getResponseBody()));
        }
        //包含检查点检测
        if (!StringUtils.isEmpty(checkPoint.getContainCheckPoint())){
            checkPointResult.addCheckPointResultDetail(containCheck(checkPoint.getContainCheckPoint(), httpResponseDetail.getResponseBody(),envId,projectId));
        }
        //不包含检查点检测
        if (!StringUtils.isEmpty(checkPoint.getNoContainCheckPoint())){
            checkPointResult.addCheckPointResultDetail(noContainCheck(checkPoint.getNoContainCheckPoint(), httpResponseDetail.getResponseBody(),envId,projectId));
        }
        //JsonPath检查点检测
        if (!StringUtils.isEmpty(checkPoint.getJsonPathCheckPoint())) {
            Object jsonObject = Configuration.defaultConfiguration().jsonProvider().parse(httpResponseDetail.getResponseBody());
            checkPointResult.addCheckPointResultDetail(jsonPathCheck(checkPoint.getJsonPathCheckPoint(), jsonObject,envId,projectId));
        }

        return checkPointResult;
    }

    public CheckPointResultDetail jsonPathCheck(String jsonPathCheckPoint, Object jsonObject,Long envId,Long projectId) {
        String expression = jsonPathCheckPoint;
        try {
            expression = completeExpressionUtil.completeVariable(
                    expression,
                    envId,
                    projectId)
            ;
            //todo 李迪凡 先将result中的动态变量都还原出来  （先后顺序不能乱）得先还原动态变量
            expression = completeExpressionUtil.completeJsonPathExpression(
                    expression,
                    jsonObject
            );
        } catch (IllegalArgumentException e) {
            return new CheckPointResultDetail(
                    false,
                    CheckPointType.JSONPATH_CHECK,
                    expression,
                    "解析失败",
                    String.format("JsonPath检查点，检查未通过，不能填写空的JsonPath,错误的jsonpath为：%s", jsonPathCheckPoint)
            );
        } catch (PathNotFoundException e) {
            return new CheckPointResultDetail(
                    false,
                    CheckPointType.JSONPATH_CHECK,
                    expression,
                    "解析失败",
                    String.format("JsonPath检查点，检查未通过，jsonpath取不到任何值，错误的jsonpath为：%s", e.getMessage().substring(e.getMessage().indexOf(":")))
            );
        }
        try {
            ScriptEngine jsEngine = getScriptEngine();
            Object eval = jsEngine.eval(expression);
            if (eval instanceof Boolean) {
                if ((Boolean) eval) {
                    return new CheckPointResultDetail(
                            true,
                            CheckPointType.JSONPATH_CHECK,
                            jsonPathCheckPoint,
                            expression,
                            "JsonPath检查点，检查通过"
                    );
                } else {
                    return new CheckPointResultDetail(
                            false,
                            CheckPointType.JSONPATH_CHECK,
                            jsonPathCheckPoint,
                            expression,
                            "JsonPath检查点，检查未通过,计算结果为false"
                    );
                }
            } else {
                return new CheckPointResultDetail(
                        false,
                        CheckPointType.JSONPATH_CHECK,
                        jsonPathCheckPoint,
                        expression,
                        "JsonPath检查点，检查未通过，JsonPath的返回值不是一个布尔类型"
                );
            }
        } catch (ScriptException e) {
            throw new CheckPointException("初始化js引擎失败" + e);
        }
    }

    public ScriptEngine getScriptEngine() throws ScriptException {
        ScriptEngine jsEngine = ScriptUtil.createJsEngine();
        jsEngine.eval(baseJsString);
        return jsEngine;
    }


    public List<CheckPointResultDetail> containCheck(String containCheckPoint, String responseBody,Long envId,Long projectId) {
        List<CheckPointResultDetail> resultDetails = new ArrayList<>();
        String[] split = containCheckPoint.split(",");
        for (String containExpression : split) {
           String parseContainExpression =  completeExpressionUtil.completeVariable(
                    containExpression,
                    envId,
                    projectId)
            ;
            if (!StringUtils.isEmpty(responseBody)&&responseBody.contains(parseContainExpression)) {
                resultDetails.add(new CheckPointResultDetail(
                        true,
                        CheckPointType.CONTAIN_CHECK,
                        containExpression,
                        parseContainExpression,
                        "包含检查点，检查通过"
                ));
            } else {
                resultDetails.add(new CheckPointResultDetail(
                        false,
                        CheckPointType.CONTAIN_CHECK,
                        containExpression,
                        parseContainExpression,
                        "包含检查点，检查未通过，不包含目标值"
                ));
            }
        }
        return resultDetails;
    }

    public List<CheckPointResultDetail> noContainCheck(String noContainCheckPoint, String responseBody,Long envId,Long projectId) {
        List<CheckPointResultDetail> resultDetails = new ArrayList<>();
        String[] split = noContainCheckPoint.split(",");
        for (String noContainExpression : split) {
            String parseNoContainExpression =  completeExpressionUtil.completeVariable(
                    noContainExpression,
                    envId,
                    projectId)
            ;
            if (!StringUtils.isEmpty(responseBody)&&responseBody.contains(parseNoContainExpression)) {
                resultDetails.add( new CheckPointResultDetail(
                        false,
                        CheckPointType.NO_CONTAIN_CHECK,
                        noContainExpression,
                        parseNoContainExpression,
                        "不包含检查点，检查未通过"
                ));
            } else {
                resultDetails.add( new CheckPointResultDetail(
                        true,
                        CheckPointType.NO_CONTAIN_CHECK,
                        noContainExpression,
                        parseNoContainExpression,
                        "不包含检查点，检查通过"
                ));
            }
        }
        return resultDetails;
    }


    public CheckPointResultDetail nullCheck(String responseBody) {
        boolean isNull = false;
        if (StringUtils.isBlank(responseBody)) {
            isNull = true;
        } else {
            try {
                JSONObject jsonObject = JSON.parseObject(responseBody);
                isNull = jsonObject.isEmpty();
            } catch (JSONException ignored) {
            }
            try {
                JSONArray jsonArray = JSON.parseArray(responseBody);
                isNull = jsonArray.isEmpty();
            } catch (JSONException ignored) {
            }
        }
        if (isNull) {
            return new CheckPointResultDetail(
                    false,
                    CheckPointType.NULL_CHECK,
                    "",
                    "",
                    String.format("不为空检查点，检查未通过，请求结果：%s", responseBody)
            );
        } else {
            return new CheckPointResultDetail(
                    true,
                    CheckPointType.NULL_CHECK,
                    "",
                    "",
                    "不为空检查点，检查通过"
            );
        }
    }


    public CheckPointResultDetail exceptionCheck(HttpResponseDetail httpResponseDetail) {
        if (httpResponseDetail.getStatusCode()!=null && httpResponseDetail.getStatusCode().value() == HttpStatus.OK.value()) {
            return new CheckPointResultDetail(
                    true,
                    CheckPointType.EXCEPTION_CHECK,
                    "",
                    "",
                    "异常检查点，检查通过"
            );
        } else {
            return new CheckPointResultDetail(
                    true,
                    CheckPointType.EXCEPTION_CHECK,
                    "",
                    "",
                    String.format("异常检查点，检查未通过，Http请求错误，错误码：%d,请求结果：%s", httpResponseDetail.getStatusCode()==null?0:httpResponseDetail.getStatusCode().value(), httpResponseDetail.getResponseBody())
            );
        }
    }


}
