package net.northking.cctp.se.script.service;


import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.common.util.UUIDUtil;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.IScriptStep;
import net.northking.cctp.element.core.ITestTarget;
import net.northking.cctp.element.core.exception.AssertException;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.exception.KeywordNotFoundException;
import net.northking.cctp.element.core.keyword.KeywordArgument;
import net.northking.cctp.element.core.type.LibraryConstant;
import net.northking.cctp.se.component.ComponentLibrary;
import net.northking.cctp.se.component.ComponentManagementService;
import net.northking.cctp.se.component.InternalComponent;
import net.northking.cctp.se.config.AtuServerConfig;
import net.northking.cctp.se.debug.bean.DebugerConst;
import net.northking.cctp.se.debug.bean.DebuggerRequest;
import net.northking.cctp.se.device.bean.DeviceInfo;
import net.northking.cctp.se.device.service.DeviceConnection;
import net.northking.cctp.se.file.Attachment;
import net.northking.cctp.se.lifecycle.EngineRuntime;
import net.northking.cctp.se.log.bean.StepLog;
import net.northking.cctp.se.script.bean.*;
import net.northking.cctp.se.script.core.*;
import net.northking.cctp.se.util.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.NoSuchSessionException;
import org.springframework.boot.logging.LogLevel;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * 脚本执行器
 */
@Slf4j
public class ScriptRuntimeExecutor implements ScriptExecutor {
    private String identifier;
    private String projectId; //项目ID,用于获取或更新项目级全局变量值
    private String tenantId;    // 通过projectId获取，用于
    private String envId;     // 环境id
    private ApplicationContext applicationContext;
    private Script script;
    private boolean record;
    private boolean saveSysLog; // 保存系统日志，计划执行可能用上
    private boolean snapshot;   // 是否截图
    private boolean errorSnapshot;  // 错误异常发生后截图
    private String requestId;   // 请求标志id
    private String currentStepTitle;    // 当前步骤名称
    private String currentStepType;     // 当前步骤类型
    private String currentStepId;       // 当前步骤的id
    private ScriptStep nextStep;

    private String startStepId; // 第一步的id
    private boolean startFlag; // 如果startStepId找到，那么设置成true
    private boolean singleStepRelay;    // 是否但不会放
    private String replayType = DebugerConst.ReplayType.TYPE_ALL;

    private StepLog stepLog;    // 步骤日志内容
    private Map<String, ScriptStep>  scriptStepMap = new HashMap<>();

    private ScriptRuntimeExecutor parentContext; // 父级执行器

    private Map<String,String> variables = new HashMap<>();     // 变量列表
    private Map<String,String> projectGlobalVars = new HashMap<>(); // 项目全局变量
    private Map<String,ScriptResource> dependScriptResourceMap = new HashMap<>();   // 依赖
    private Map<String,Variable> variableMap = new HashMap<>(); // 脚本变量信息
    private Map<String, VariableDto> globalVarmap = new HashMap<>();   // 全局变量信息
    @VisibleForTesting
    private List<ScriptStep> commands = new ArrayList<>();

    private Map<String,Object> contextVars = new HashMap<>(); // 上下文变量集合
    private Map<String, ComponentLibrary> libraryInstanceMap = new HashMap<>(); //
    private Map<String, ScriptRuntimeExecutor> sonExecutorMap = new HashMap<>();
    private ScriptRuntimeListener listener;
    private String currentTaskId;

    public ScriptRuntimeExecutor(String identifier, String projectId, String envId, Script script) {
        this(identifier, projectId, script, false, false, false);
    }

    private String resourcePathPrefix = "resource" + File.separator;

    public ScriptRuntimeExecutor(String identifier, String projectId, Script script, boolean record, boolean saveSysLog, boolean snapshot) {
        this.identifier = identifier;
        this.projectId = projectId;
        this.script = script;
        this.record = record;
        this.saveSysLog = saveSysLog;
        this.snapshot = snapshot;
        applicationContext = SpringUtils.getContext();
    }

    public ScriptRuntimeExecutor(String identifier, String projectId, String envId,
                                 Script script, boolean errorSnapshot,
                                 boolean snapshot, ScriptRuntimeListener listener, ScriptRuntimeExecutor parent) {
        this.identifier = identifier;
        this.projectId = projectId;
        this.envId = envId;
        this.script = script;
        this.errorSnapshot = errorSnapshot;
        this.snapshot = snapshot;
        this.saveSysLog = false;
        applicationContext = SpringUtils.getContext();
        this.listener = listener;
        this.parentContext = parent;
    }

    public ScriptRuntimeExecutor(String identifier, String projectId, String envId,
                                 Script script, boolean errorSnapshot, boolean snapshot) {
        this.identifier = identifier;
        this.projectId = projectId;
        this.envId = envId;
        this.script = script;
        this.errorSnapshot = errorSnapshot;
        this.snapshot = snapshot;
        this.saveSysLog = false;
        applicationContext = SpringUtils.getContext();
    }

    @Override
    public void setCurrentTaskId(String taskId) {
        this.currentTaskId = taskId;
    }

    public ScriptRuntimeExecutor getParent() {
        return parentContext;
    }

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    @Override
    public String getRuntimeIdentifier() {
        return identifier;
    }

    public Script getScript() {
        return script;
    }


    public String getProjectId() {
        return projectId;
    }

    public void setProjectId(String projectId) {
        this.projectId = projectId;
    }


    public void setParentContext(ScriptRuntimeExecutor parentContext) {
        this.parentContext = parentContext;
    }

    public void setLibraryInstanceMap(Map<String, ComponentLibrary> libraryInstanceMap) {
        this.libraryInstanceMap = libraryInstanceMap;
    }

    /**
     * 数据初始化，业务调用
     *
     * @param data
     */
    @Override
    public void initData(Map<String, Object> data) {

    }

    private void initVariables(Variable[] vars) {
        log.info("Init variables ...");
        if (vars != null && vars.length > 0) {
            for (Variable var : vars) {
                variableMap.put(var.getName(), var);
                if (StringUtils.isBlank(var.getValue())) {  // 如果变量的value不为空，也就是直接获取value   单步回放用到
                    var.setValue(var.getDefaultValue());
                }
                variables.put(var.getName(), var.getValue());
            }
        }
        log.info("Init variables 完成，最终结果：variableMap  ----->{}", JsonUtils.toJson(variableMap));
        log.info("Init variables 完成，最终结果：variables  ----->{}", JsonUtils.toJson(variables));
    }

    private void initGlobalSetting() {
        script.getGlobalSettings();
    }

    /**
     * 初始化脚本信息
     *
     * @param script
     */
    @Override
    public void init(Script script) {
        try {
            doLog(LogLevel.INFO.name(), String.format("正在使用执行机：%s",EngineRuntime.getRuntime().getEngineInfo().getIpAddress()));
            initVariables(script.getVariables());        // 初始化变量
            initScriptResources(script.getResources());  // 初始化脚本资源
            initScriptSteps(script.getCommands());      // 初始化脚本步骤
            initTenant();
            initEnv();      // 初始化环境信息
        } catch (Exception e) {
            log.error("初始化异常", e);
            // 输出失败日志，reply_log
            doLog(LogLevel.ERROR.name(), e.getMessage());
            // 输出解析失败日志, reply
            StepExecuteResult result = new StepExecuteResult();
            result.setSuccess(false);
            result.setExecuted(true);
            result.setFinish(true);
            result.setMsg(e.getMessage());
            result.setRequestId(requestId);
            result.setRetId(UUIDUtil.create32UUID());
            result.setScriptId(script.getId());
            if (null != getCurrentRequest() && null != getCurrentRequest().getData().get(DebugerConst.ReqConst.KEY_REPLAY_FROM_STEP_ID)) {
                result.setStepId(getCurrentRequest().getData().get(DebugerConst.ReqConst.KEY_REPLAY_FROM_STEP_ID).toString());
                result.getResult().put(DebugerConst.ReplyConst.KEY_STEP_ID, result.getStepId());
            }
            doReply(result);

            throw e;
        }
    }

    private void initEnv() {
        setContextVariable(KEY_ENV_ID, this.envId);
    }

    /**
     * 设置租户信息
     */
    private void initTenant() {
        if (ObjectUtils.isNotEmpty(getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT))) {
            this.tenantId = (String)getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT);
        } else {
            // 通过projectId获取
            AtuServerConfig config = SpringUtils.getBean(AtuServerConfig.class);
            String url = config.getServerUrl() + config.getProjectInfo();
            RestTemplate restTemplate = new RestTemplate();
            try {
                ResultWrapper<Map<String, Object>> wrapper = restTemplate.getForObject(url, ResultWrapper.class, this.projectId);
                if (wrapper != null && wrapper.isSuccess() && wrapper.getData() != null) {
                    if (wrapper.getData().containsKey("tenantId") && wrapper.getData().get("tenantId") != null) {
                        this.tenantId = (String)wrapper.getData().get("tenantId");
                    }
                }
                log.debug("请求项目信息：{}", JsonUtils.toJson(wrapper));
            } catch (Exception e) {
                log.error("请求获取项目信息失败", e);
            }
        }
        log.debug("initTenant的projectId:{}",projectId);
        setContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT, this.tenantId);
    }

    /**
     * 获取脚本资源
     * 通过resource字段，预先将资源下载到本地
     */
    private void initScriptResources(Map<String, List<ScriptResource>> resources) {
//        Map<String, List<ScriptResource>> resources = script.getResources();
        for (List<ScriptResource> resourceList : resources.values()) {
            for (ScriptResource resource : resourceList) {
                dependScriptResourceMap.put(resource.getResId(), resource);
            }
        }
        log.info("initScriptResources完成，最终结果：dependScriptResourceMap  ----->{}", JsonUtils.toJson(dependScriptResourceMap));
//        doLog("初始化脚本资源成功");
    }

    /**
     * @param resource
     */
    private File resourceDownload(ScriptResource resource) {
        AtuServerConfig config = SpringUtils.getBean(AtuServerConfig.class);
        RestTemplate restTemplate = new RestTemplate();
        String url = config.getServerUrl() + config.getFileDownloadPath() + resource.getResUri();
        ResponseEntity<byte[]> resultEntity = null;
        try {
            resultEntity = restTemplate.getForEntity(url, byte[].class);
        } catch (HttpClientErrorException e) {
            if (HttpStatus.NOT_FOUND == e.getStatusCode()) {
                throw new ExecuteException("下载脚本资源失败，脚本已被删除或者脚本不存在", e);
            }
            throw new ExecuteException("下载脚本资源失败", e);
        }
        String filename = resource.getResUri();
        File targetFile = new File(resourcePathPrefix + this.identifier + File.separator + resource.getResType() + File.separator + filename);
        if (HttpStatus.OK.equals(resultEntity.getStatusCode())) {
            HttpHeaders headers = resultEntity.getHeaders();
            String originName = headers.get("Content-Disposition").get(0).split("=", 2)[1];   // 获取文件名字，attachment;filename=xxxx.xxx
            File parentFile = targetFile.getParentFile();
            if (!parentFile.exists()) { // 父级目录不存在
                parentFile.mkdirs();
            }
            byte[] content = resultEntity.getBody();
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(targetFile);
                fos.write(content);
                fos.flush();
            } catch (IOException e) {
                log.error("资源文件【{}】-【{}】写入失败", filename, originName, e);
                // todo: doLog or doReply
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        log.error("关闭文件输出流失败", e);
                        // todo: doLog or doReply
                    }
                }
            }
            log.error("资源文件【{}】写到本地成功，路径：{}", filename,targetFile.getAbsolutePath());
        } else {
            log.error("资源文件【{}】下载不成功", filename);
        }
        return targetFile;
    }

    private void initScriptSteps(ScriptStep[] steps) {
        try {
            log.info("Init script steps ...");
            if (steps != null && steps.length > 0) {
                for (int i = 0; i < steps.length; i++) {
                    ScriptStep step = steps[i];
                    if (!step.isSkip()) {    // 判断是否步骤已被注释
                        if (InternalComponent.InternalKeyword.KEYWORD_GROUP.equalsIgnoreCase(step.getCommand()) && step.getChildren().length == 0) {
                            log.debug("分区【{}】内容为空，不加入到步骤集合中。。。", step.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME));
                        } else {
                            if (null == parentContext) { // 顶层脚本
                                if (i + 1 >= steps.length) {   // 最后一步
                                    step.setFinalStep(true);
                                } else {
                                    ScriptStep nextStep = steps[i + 1];
                                    step.setParentNextStepId(nextStep.getId());
                                }
                            }
                            commands.add(step);
                            initScriptStep(step);
                        }
                    }
                }
                log.info("script设置完成，该脚本准备的环境如下：commands:{}", JsonUtils.toJson(commands));
                log.info("script设置完成，该脚本准备的环境如下：dependScriptResourceMap:{}", JsonUtils.toJson(dependScriptResourceMap));
                log.info("script设置完成，该脚本准备的环境如下：sonExecutorMap:{}", JsonUtils.toJson(sonExecutorMap));
            }
        } catch (Exception e) {
            throw new ExecuteException(e.getMessage(), e);
        }
        if (parentContext == null) {
            doLog(LogLevel.INFO.name(), String.format("正在使用执行机：%s",EngineRuntime.getRuntime().getEngineInfo().getIpAddress()) + "\n初始化脚本步骤成功");
        }
    }

    private void initScriptStep(ScriptStep scriptStep) {
        try {
            ScriptStep stepClone = ObjectUtil.cloneByStream(scriptStep);
             scriptStepMap.put(scriptStep.getId(), stepClone);
            // 接口组件输入的初始化
            String input = scriptStep.getArgs().get(DebugerConst.ReqConst.KEY_INPUTS);
            if (StringUtils.isNotBlank(input)) {
                Map<String, String> inputMap = JsonUtils.readMap(input, String.class);
                scriptStep.getInputMap().putAll(inputMap);
            }
            if (InternalComponent.NK_INTERNAL_LIB.equalsIgnoreCase(scriptStep.getComponentLib())) { // 是否内部组件
                if (scriptStep.getChildren() != null && scriptStep.getChildren().length > 0) {    // 分区
                    initChildren(scriptStep, stepClone);
                } else {    // 业务组件
                    analyseBizCmpt(scriptStep);
                }
            } else {
                // 当前步骤是一个基础组件，处理组件库实例Library
                addLibrary2Map(scriptStep);
                // 解析逻辑组件
                if (scriptStep.getChildren() != null && scriptStep.getChildren().length > 0) {
                    initChildren(scriptStep, stepClone);
                }
                // 将commandName添加到tile，组件使用 pc组件用到
                if (StringUtils.isNotBlank(scriptStep.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME))) {
                    scriptStep.getArgs().put(DebugerConst.ReqConst.KEY_TITLE, scriptStep.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME));
                }
            }
        } catch (Exception e) {
            String msg = String.format("步骤【%s】初始化失败 %s ", scriptStep.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME), e.getMessage());
            log.error("初始化步骤失败，e,{}",e);
            throw new ExecuteException(msg, e);
        }
    }

    private void initChildren(ScriptStep scriptStep, ScriptStep stepClone) {
        ScriptStep[] children = stepClone.getChildren();
        for (int i = 0; i < children.length; i++) {
            ScriptStep child = children[i];
            if (!child.isSkip()) {
                stepClone.addChildStep(child);
                // 判断是否最后一步
                scriptStep.addChildStep(child);
                // 处理if elseIF的子步骤
                if (LogicStepType._if.name().equalsIgnoreCase(scriptStep.getCommand())
                        || LogicStepType._else_if.name().equalsIgnoreCase(scriptStep.getCommand())) {
                    scriptStep.addIfStep(scriptStep.getCommand(), child);
                }
                initScriptStep(child);
            }
        }
    }

    /**
     * 解析业务组件
     * 下载
     * 参数
     * 步骤
     * 业务步骤
     *
     * @param scriptStep
     */
    private void analyseBizCmpt(ScriptStep scriptStep) {
        String resId = scriptStep.getArgs().get(DebugerConst.ReqConst.KEY_RES_ID);
        if (StringUtils.isNotBlank(resId)) {
            ScriptResource resource = dependScriptResourceMap.get(resId);
            if (resource != null) { // 资源信息存在
                if (StringUtils.isNotBlank(resource.getResName())) {
                    scriptStep.getArgs().put(DebugerConst.ReqConst.KEY_COMMAND_NAME, resource.getResName());
                }
                File resourceFile = resourceDownload(resource);
                if (resourceFile.exists() && resourceFile.length() > 0) {
                    // 读取文件
                    String scriptStr = readFile(resourceFile);
                    log.debug("resId -> {}读取到了脚本文件：{}", resId,scriptStr);
                    Object parse = JSON.parse(scriptStr);
                    JSONObject json = JSON.parseObject(parse.toString());
                    Script bizScript = json.toJavaObject(Script.class);
                    // 创建子执行器
                    ScriptRuntimeExecutor sonExecutor = new ScriptRuntimeExecutor(identifier, projectId, envId, bizScript, errorSnapshot, snapshot, this.listener, this);
                    sonExecutor.setLibraryInstanceMap(this.libraryInstanceMap);
                    sonExecutor.setRequestId(this.requestId);
                    sonExecutor.init(bizScript);
//                    log.debug("analyse stepId -> {}, requestId -> {}", scriptStep.getId(), sonExecutor.getRequestId());
                    sonExecutorMap.put(scriptStep.getId(), sonExecutor);
                } else {
                    log.debug("resId -> {}没有写入对应的resourceFile", resId);
                }
            } else {
               log.debug("resId -> {}, 没有拿到ScriptResource", resId);
            }
        }
        // 由于业务组件的args没有inputs字段，参数直接放到args下，特殊处理，但是做法不安全
        Map<String, String> args = scriptStep.getArgs();
        for (String key : args.keySet()) {
            if (DebugerConst.ReqConst.KEY_RES_ID.equalsIgnoreCase(key)) {
                continue;
            }
            scriptStep.getInputMap().put(key, args.get(key));
        }
    }

    /**
     * @param resourceFile
     * @return
     */
    private String readFile(File resourceFile) {
        InputStream is = null;
        try {
            is = new FileInputStream(resourceFile);
            return IoUtil.readUtf8(is);
        } catch (Exception e) {
            throw new ExecuteException(String.format("读取资源【%s】文件失败", resourceFile.getName()), e);
        } finally {
            IoUtil.closeIfPosible(is);
        }
    }

    /**
     * 清理执行过程中产生的业务组件、接口调用等脚本调用的资源文件
     */
    @Override
    public void clearResource() {
        File directory = new File(resourcePathPrefix + this.identifier);
        delete(directory);
    }

    private void delete(File directory) {
        if (directory.exists()) {
            for (File file : directory.listFiles()) {
                if (file.isDirectory()) {
                    delete(file);
                } else {
                    if (!file.delete()) {
                        log.debug("资源文件删除失败 -> {}", directory.getAbsolutePath());
                    }
                }
            }
            if (!directory.delete()) {
                log.debug("资源文件删除失败 -> {}", directory.getAbsolutePath());
            }
        }
    }

    private void addLibrary2Map(ScriptStep scriptStep) {
        if (scriptStep != null) {
            String componentLib = scriptStep.getComponentLib();
            if (!InternalComponent.NK_INTERNAL_LIB.equalsIgnoreCase(componentLib)) {
                try {
                    if (!libraryInstanceMap.containsKey(componentLib)) {
                        ComponentLibrary library =
                                SpringUtils.getBean(ComponentManagementService.class)
                                        .newInstance(componentLib);
                        library.refreshThreadCache();
                        libraryInstanceMap.put(componentLib, library);
                    } else {
                        libraryInstanceMap.get(componentLib).refreshThreadCache();
                    }
                } catch (Exception e) {
                    log.error("组件库实例创建失败");
                    throw new ExecuteException("组件库实例创建失败 " + e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 处理步骤的参数变量
     *
     * @param scriptStep
     */
    private void extractVariables(ScriptStep scriptStep, Map<String, KeywordArgument> argumentMap) {
        //输入参数-> 通过服务端获取替换 步骤里边
        Map<String, String> args = scriptStep.getArgs();  // 步骤的参数
        if (args != null) {
            for (String arg : args.keySet()) {
                KeywordArgument keywordArgument = argumentMap.get(arg); // 通过参数命名获取关键字的对应的输入参数
                String expression = args.get(arg);
                if (keywordArgument == null || !keywordArgument.isExpression()) {
                    expression = variableReplace(expression);
                }
                // 替换
                args.put(arg, expression);
            }
            // 统一处理timeout问题
            if (!args.containsKey(DebugerConst.ReqConst.KEY_TIMEOUT)) {
                args.put(DebugerConst.ReqConst.KEY_TIMEOUT, String.valueOf(getGlobalTimeout()));
            }
        }
        // 步骤的操作对象中value变量的处理
        List<StepTarget> targets = scriptStep.getTargets();
        if (targets != null) {
            for (StepTarget target : targets) {
                String expression = target.getValue();
                expression = variableReplace(expression);
                target.setValue(expression);
            }
        }
    }

    /**
     * @param expression
     * @return
     */
    @VisibleForTesting
    private int invokeTimes = 0;

    private String variableReplace(String expression) {
        if (StringUtils.isBlank(expression)) {
            return expression;
        }
        invokeTimes++;
        // 全局参数
        if (RegexUtils.containGlobalVar(expression)) {
            Set<String> globalVars = RegexUtils.extractGlobalVar(expression);
            // 请求服务端并替换
            expression = requestAndReplace(globalVars, false, expression);
        }
        // 脚本参数
        if (RegexUtils.containScriptVar(expression)) {
            Set<String> scriptVars = RegexUtils.extractScriptVar(expression);
            //从上下文替换 从Variable获取
            for (String sv : scriptVars) {
                String value = variables.get(sv);
                String format = String.format("${%s}", sv);
                if (null != value) {
                    expression = expression.replace(format, value);
                }
            }
            if (RegexUtils.containGlobalVar(expression) || RegexUtils.containScriptVar(expression)) {
                if (invokeTimes <= 5) { // 可能存在互相引用导致无限递归，目前暂定最多5次
                    expression = variableReplace(expression);
                }
            }
        }
        invokeTimes = 0;
        return expression;
    }

    /**
     * 请求服务获取变量值
     *
     * @param vars        变量名集合
     * @param isScriptVar 是否脚本变量，true 脚本变量 | false 全局变量
     * @param expression
     * @return
     */
    private String requestAndReplace(Set<String> vars, boolean isScriptVar, String expression) {
        for (String varName : vars) {
            String value = getGlobalVarFromServer(isScriptVar, varName);
            String format;
            if (isScriptVar) {
                format = String.format("${%s}", varName);
            } else {
                format = String.format("#{%s}", varName);
            }
            expression = expression.replace(format, null != value ? value : "");
        }
        return expression;
    }

    /**
     * 通过服务器获取全局参数
     * @param isScriptVar
     * @param varName
     * @return
     */
    private String getGlobalVarFromServer(boolean isScriptVar, String varName) {
        AtuServerConfig config = SpringUtils.getBean(AtuServerConfig.class);
        RestTemplate restTemplate = new RestTemplate();
        String url = config.getServerUrl() + config.getGlobalVarPath();
        VariableDto param = new VariableDto();
        param.setName(varName);
        param.setProjectId(projectId);
        param.setVariableType(isScriptVar);
        if (isScriptVar) {  // 如果是脚本变量，那么需要增加脚本id
            param.setScriptId(script.getId());
        }
        log.debug("全局参数请求地址：{}，全局参数请求内容：{}", url, JsonUtils.toJson(param));
        ResponseEntity<VariableDto> resEntity = restTemplate.postForEntity(url, param, VariableDto.class);
        if (HttpStatus.OK.equals(resEntity.getStatusCode())) {
            if (resEntity.getBody() != null) {
                if (resEntity.getBody().isExist()) {
                    globalVarmap.put(varName, resEntity.getBody());
                    return resEntity.getBody().getValue();
                } else {
                    throw new ExecuteException(String.format("全局参数[%s]已被删除", varName));
                }
            }
        }
        return null;
    }

    private void clear() {
        scriptStepMap.clear();
        variables.clear();
        variableMap.clear();
        projectGlobalVars.clear();
        globalVarmap.clear();
        dependScriptResourceMap.clear();
        commands.clear();
        libraryInstanceMap.clear();
        clearResource();
    }

    private int getGlobalTimeout() {
        Object timeout = script.getGlobalSettings().get(DebugerConst.ReqConst.KEY_TIMEOUT);
        if (timeout instanceof Integer) {
            return (Integer) timeout;
        }
        return 30;
    }

    private boolean getGlobalIgnoreError() {
        Map<String, Object> globalSettings = script.getGlobalSettings();
        if (null != globalSettings) {
            Object ignoreError = globalSettings.get(DebugerConst.ReqConst.KEY_IGNORE_ERROR);
            if (ignoreError instanceof Boolean) {
                return (Boolean) ignoreError;
            }
        }
        return false;
    }

    @Override
    public void setIgnoreError(boolean ignoreError) {
        script.getGlobalSettings().put(DebugerConst.ReqConst.KEY_IGNORE_ERROR, ignoreError);
    }

    @Override
    public void setScript(Script script) {
        this.script = script;
    }

    @Override
    public ScriptExecuteResult runScript(Script script, ScriptExecuteResult ret) {
        startFlag = true;   // 直接回放
        this.replayType = DebugerConst.ReplayType.TYPE_ALL;
        if (null == ret) {
            ret = new ScriptExecuteResult();
        }
        ret.setStartTime(System.currentTimeMillis());
        if (saveSysLog) { //TODO 如果需要采集应用日志，启动采集日志操作
            log.info("开始启动采集日志");
            doLog(LogLevel.INFO.name(), "已启动日志采集功能");
        }
        try {
            // 执行步骤
            executeSteps(script, ret);
        } catch (AssertException | ExecuteException e) {
            throw e;
        } catch (Exception e) {
            throw new ExecuteException(e.getMessage(), e);
        } finally {
            if (saveSysLog) {// TODO 如果需要采集应用日志，结束采集日志操作，并上传文件
                log.info("结束采集日志");
            }
            // 返回结果
            ret.setEndTime(System.currentTimeMillis());
        }
        return ret;
    }

    private void executeSteps(Script script, ScriptExecuteResult ret) {
        StepExecuteResult stepResult = null;
        ScriptStep step = null;
        log.debug("最终解析出来的脚本步骤为：{}",JsonUtils.toJson(commands));
        for (int i = 0; i < commands.size(); i++) {
            try {
                step = commands.get(i);
                stepResult = createStepResult(step, commands, i + 1);     // 脚本步骤
                ret.addStepResult(stepResult);
                // 执行步骤
                executeStep(step, ret, stepResult);
                if (null == parentContext && startFlag && nextStep == null) {
                    if (i + 1 <= commands.size() -1) {
                        nextStep = commands.get(i + 1);
                    }
                }
                if (Thread.currentThread().isInterrupted()) {   // 中止
                    throw new ExecuteException("执行被中止");
                }
            } catch (Exception e) {
                if (getGlobalIgnoreError()) {
                    log.error("ignoreError -> 【{}】,继续执行", getGlobalIgnoreError());
                    log.error(e.getMessage(), e);
                } else {
                    if (stepResult == null) {
                        stepResult = new StepExecuteResult();
                        stepResult.setScriptId(script.getId());
                        stepResult.setStepId(null != step ? step.getId() : null);
                        stepResult.setMsg(e.getMessage());
                        stepResult.getResult().put(DebugerConst.ReplyConst.KEY_STEP_ID, stepResult.getStepId());
                    }
                    if (e instanceof AssertException) {
                        // 断言异常
                        doLog(LogLevel.ERROR, e.getMessage());
                        throw (AssertException) e;
                    } else {
                        throw new ExecuteException(e.getMessage(), e);
                    }
                }
            } finally {
                if (startFlag && step != null
                        && !InternalComponent.InternalKeyword.KEYWORD_GROUP.equalsIgnoreCase(step.getCommand())) {
                    doReply(stepResult);
                }
                initCurrentParams();
            }
            if (startFlag && singleStepRelay) {
                replyNextStep(nextStep);
                this.singleStepRelay = false;
                nextStep = null;
                break;
            }
        }
        // 销毁
        if (null == parentContext) {
            destroyLibraryInstance();
        }
        if (Thread.currentThread().isInterrupted()) { // 中断后恢复
            Thread.interrupted();
        }
    }

    public void initCurrentParams() {
        currentStepTitle = null;
        currentStepType = null;
        currentStepId = null;
    }

    /**
     * 增加当前步骤返回
     *
     * @param step
     */
    private void replyNextStep(ScriptStep step) {
        if (step == null) {
            return;
        }
        if (null == parentContext && startFlag) {
            HashMap<String, String> replyMap = new HashMap<>();
            replyMap.put(DebugerConst.KEY_REQUESTID, this.requestId);
            replyMap.put(DebugerConst.ReplyConst.KEY_STEP_ID, step.getId());
            boolean logicFlag = LogicStepType._if.name().equals(step.getCommand()) || LogicStepType._for.name().equals(step.getCommand());
            if (step.getChildSteps() != null && !step.getChildSteps().isEmpty()
                    && !logicFlag) {
                replyMap.put(DebugerConst.ReplyConst.KEY_STEP_ID, step.getChildSteps().get(0).getId());
            }
            doReply("reply_current_step", LogLevel.INFO.name(), replyMap);
        }
    }

    private StepExecuteResult createStepResult(ScriptStep step, List<ScriptStep> steps, int nextIdx) {
        StepExecuteResult stepResult = createStepResult(step);
        // 确定个最后一步
        return stepResult;
    }

    private StepExecuteResult createStepResult(ScriptStep step) {
        StepExecuteResult stepResult = new StepExecuteResult();
        stepResult.setCommand(step.getCommand());
        stepResult.setComponentLib(step.getComponentLib());
        stepResult.setScriptId(script.getId());
        stepResult.setRetId(UUIDUtil.create32UUID());
        stepResult.setSuccess(true);
        stepResult.setRequestId(requestId);
        stepResult.setStepId(step.getId());
        stepResult.getResult().put(DebugerConst.ReplyConst.KEY_STEP_ID, step.getId());
        stepResult.getResult().put(DebugerConst.ReplyConst.KEY_VARIABLES, variables);
        stepResult.getResult().put(DebugerConst.ReplyConst.KEY_COMMAND, step.getCommand());
        currentStepTitle = step.getExtAttr(DebugerConst.ReqConst.KEY_TITLE);
        currentStepType = step.getExtAttr(DebugerConst.ReqConst.KEY_STEP_TYPE);
        currentStepId = step.getId();
        return stepResult;
    }

    /**
     * 销毁执行器的组件库实例
     */
    private void destroyLibraryInstance() {
        if (libraryInstanceMap.size() > 0) {
            for (String key : libraryInstanceMap.keySet()) {
                libraryInstanceMap.get(key).destroy();
            }
        }
    }


    /**
     * 运行脚本步骤
     *
     * @param step                脚本步骤信息
     * @param scriptExecuteResult 脚本执行结果
     * @param stepResult          步骤执行结果
     */
    private void executeStep(ScriptStep step, ScriptExecuteResult scriptExecuteResult, StepExecuteResult stepResult) {
         log.debug("Executing script step[id:{},skip:{},component:{},keyword:{}]", step.getId(), step.isSkip(), step.getComponentLib(), step.getCommand());
        if (step.isSkip()) { // 步骤被注释，不执行
            log.info("Step[id:{}] skipped", step.getId());
            return;
        }

        String comLib = step.getComponentLib(); // 库名
        String keyword = step.getCommand();     // 关键字

        boolean enterChildren = false;
        try {

            if (Thread.currentThread().isInterrupted()) {  // 判断是否有中止标志
                throw new ExecuteException("执行被中止");
            }
            if (InternalComponent.NK_INTERNAL_LIB.equalsIgnoreCase(comLib)) {
                if (step.getChildSteps() != null && !step.getChildSteps().isEmpty()) {    // 分区
                    enterChildren = true;
                }
                if (InternalComponent.InternalKeyword.KEYWORD_INVOKE.equalsIgnoreCase(keyword)) {   // 业务组件或者接口
                    log.info("执行业务组件Step[id:{}]", step.getId());
                    if (sonExecutorMap.containsKey(step.getId())) {
                        if (null == parentContext && !startFlag) {    // 顶层脚本
                            if (DebugerConst.ReplayType.TYPE_FROM.equalsIgnoreCase(this.replayType)
                                    || DebugerConst.ReplayType.TYPE_STEP.equalsIgnoreCase(this.replayType)) {
                                startFlag = startStepId.equalsIgnoreCase(step.getId());
                                if (!startFlag) {
                                    return;
                                }
                            }
                        }
                        replyNextStep(step);
                        ScriptRuntimeExecutor sonExecutor = sonExecutorMap.get(step.getId());
                        // 变量，将子步骤的所需要的变量进行赋值，步骤的输入参数 -> 子步骤的变量 映射
                        Map<String, String> inputMap = step.getInputMap();
                        if (inputMap.size() > 0) {
                            for (String input : inputMap.keySet()) {
                                sonExecutor.setVariable(input, variableReplace(inputMap.get(input)));
                            }
                        }
                        // 上下文变量
                        for (String key : contextVars.keySet()) {
                            sonExecutor.setContextVariable(key, contextVars.get(key));
                        }
                        try {
                            ScriptExecuteResult sonScriptResult = new ScriptExecuteResult();
                            // 将子脚本的执行结果
                            stepResult.setChildResult(sonScriptResult.getStepExecuteResults());
                            // 执行
                            sonExecutor.runScript(sonExecutor.getScript(), sonScriptResult);
                            // 子脚本执行完成
                            // 添加接口的请求和响应信息
                            addInterfaceResult(step, stepResult, sonScriptResult);
                            // 将子步骤的输出赋值到父步骤的对应的值
                            Map<String, String> outputMap = step.getOutput();
                            if (outputMap.size() > 0) {
                                for (String output : outputMap.keySet()) {
                                    String sonOutput = sonExecutor.variables.get(output);
                                    String placeholder = outputMap.get(output);
                                    if (RegexUtils.containScriptVar(placeholder)) {
                                        Set<String> scriptVar = RegexUtils.extractScriptVar(placeholder);
                                        if (scriptVar.size() == 1) {
                                            for (String key : scriptVar) {
                                                if (!variables.containsKey(key)) {  // 如果脚本变量没有对应的参数，报错
                                                    String format = String.format("输出项的参数【%s】未被定义", placeholder);
                                                    throw new ExecuteException(format);
                                                }
                                                setVariable(key, sonOutput);
                                                stepResult.getOutput().put(key, sonOutput);
                                            }
                                        } else {    // 输出项的参数多于一个占位符，不符合
                                            throw new ExecuteException(String.format("输出项的参数【%s】不符合规范", placeholder));
                                        }
                                    }
                                }
                            }
                        } catch (AssertException e) {
                            stepResult.setSuccess(false);
                            throw e;
                        } catch (Exception e) {
                            stepResult.setSuccess(false);
                            throw e;
                        } finally {
                            stepResult.setExecuted(true);
                        }
                    }else {
                        log.info("Step[id:{}]没有获取到Executor", step.getId());
                    }
                }
            } else {
                log.info("执行基础组件Step[id:{}]", step.getId());
                Object ret = null;  // 执行返回
                try {
                    // from step方式重放 加上开始判断
                    if (!startFlag) {
                        if (StringUtils.isNotBlank(startStepId)) {
                            startFlag = startStepId.equalsIgnoreCase(step.getId());
                        } else {
                            startFlag = true;
                        }
                    }
                    if (startFlag) {
                        replyNextStep(step);
                        ret = doExecuteScriptStep(comLib, keyword, step, stepResult);
                        enterChildren = executeLogicCmpt(step, stepResult);
                    }
                } catch (NoSuchSessionException e) {
                    log.warn("driver失效了，准备重新创建。。。。。");
                    boolean isSuccess = ((DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN)).reconnect();
                    if (isSuccess) {
                        //重新更新driver
                        this.setContextVariable(IScriptRuntimeContext.KEY_CURRENT_DRIVER,((DeviceConnection)this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN)).getDeviceDriver());
                        ret = doExecuteScriptStep(comLib, keyword, step, stepResult);
                    }
                }
            }
            log.info("Step[id:{}]执行完毕", step.getId());
            // 循环作为逻辑组件的循环
            while (enterChildren) {
                // 执行子步骤
                List<ScriptStep> childSteps = step.getChildSteps();
                StepExecuteResult childStepResult = null;
                stepResult.setExecuted(true);
                stepResult.setAfterLogicExec(true);
                if (childSteps != null && childSteps.size() > 0) {
                    for (int i = 0; i < childSteps.size(); i++) {
                        ScriptStep childStep = childSteps.get(i);
                        try {
                            childStepResult = createStepResult(childStep, childSteps, i + 1);
                            if (stepResult.getChildResult() == null) {
                                stepResult.setChildResult(new ArrayList<>());
                            }
                            childStepResult.setStepId(childStep.getId());
                            stepResult.getChildResult().add(childStepResult);
                            executeStep(childStep, scriptExecuteResult, childStepResult);
                            if (Thread.currentThread().isInterrupted()) {   // 中止
                                throw new ExecuteException("执行被中止");
                            }
                        } catch (AssertException e) {
                            throw e;
                        } catch (ExecuteException e) {
                            throw new ExecuteException("子步骤执行失败 " + e.getMessage(), e);
                        } finally {
                            if (startFlag && childStepResult != null) {
                                doReply(childStepResult);
                            }
                        }
                        if (null == parentContext && startFlag && this.singleStepRelay && !isLogic(step.getComponentLib(), step.getCommand())) {    // 顶层 单步回放 停止
                            if (i + 1 < childSteps.size()) {
                                nextStep = childSteps.get(i + 1);
                            }
                            break;
                        }
                    }
                }
                // 再次执行逻辑组件
                enterChildren = executeLogicCmpt(step, stepResult);
            }
        } catch (AssertException e) {
            stepResult.setMsg(e.getMessage());
            scriptExecuteResult.setResultStatus(2);
            throw e;
        } catch (Exception e) {
            stepResult.setMsg(String.format("步骤执行失败:%s", e.getMessage()));
            stepResult.setSuccess(false);
            // TODO: 出错继续执行
            throw new ExecuteException(e.getMessage(), e);
        }
    }

    /**
     * 如果当前步骤是顶层脚本并且是接口，那么给步骤的执行结果加上接口请求和返回信息
     * http和socket统一处理，result增加字段：interface
     * TODO: 用到常量需要找个地方统一管理
     *
     * @param step 当前执行的步骤
     * @param stepResult 步骤结果
     * @param sonScriptResult 子步骤的结果集
     */
    private void addInterfaceResult(ScriptStep step, StepExecuteResult stepResult, ScriptExecuteResult sonScriptResult) {
        if (null == parentContext && "interface".equalsIgnoreCase(step.getExtAttr(DebugerConst.ReqConst.KEY_STEP_TYPE))) {
            JSONObject keywordReturn = new JSONObject();
            for (StepExecuteResult childResult : sonScriptResult.getStepExecuteResults()) {
                if ("NK.Interface".equalsIgnoreCase(childResult.getComponentLib())) {
                    if ("httpRequest".equalsIgnoreCase(childResult.getCommand())) { // http的处理方式
                        keywordReturn = ObjectUtil.cloneByStream((JSONObject) childResult.getKeywordReturn());
                        if (keywordReturn.containsKey("context")) {
                            keywordReturn.put("response", keywordReturn.get("context"));
                        }
                    } else if ("socketRequest".equalsIgnoreCase(childResult.getCommand())) {        // socket的处理方式
                        keywordReturn = ObjectUtil.cloneByStream((JSONObject) childResult.getKeywordReturn());
                        if (keywordReturn.containsKey("context")) {
                            JSONObject context = (JSONObject)keywordReturn.get("context");
                            JSONObject response = new JSONObject();
                            response.put("body", context);
                            keywordReturn.put("response", response);
                        }
                    }
                    keywordReturn.remove("context");
                }
            }
            if (!keywordReturn.isEmpty()) {
                stepResult.getResult().put("interface", keywordReturn);
            }
        }
    }

    /**
     * 逻辑组件的执行
     *
     * @param step
     * @param stepResult
     * @return
     */
    public boolean executeLogicCmpt(ScriptStep step, StepExecuteResult stepResult){
        // if组件和for组件处理
        String command = step.getCommand();
        //
        LogicStepType logicStepType = LogicStepType.get(command);
        if (null == logicStepType) {
            return false;
        }
        if (StringUtils.isBlank(step.getIfStep())) {
            step.setIfStep(command);
        }
        String ifStep = step.getIfStep();
        // for逻辑
        if(LogicStepType._for.name().equalsIgnoreCase(command)) {
            boolean forFlag = (boolean)stepResult.getKeywordReturn();
            int idx = 0;
            if (step.getArg(KEY_FOR_CURRENT_INDEX) != null) {
                idx = Integer.parseInt(step.getArg(KEY_FOR_CURRENT_INDEX));
            } else {
                step.getArgs().put(KEY_FOR_CURRENT_INDEX, String.valueOf(idx));
            }
            if (forFlag && idx > 0) {
                StepExecuteResult forResult = createStepResult(step);
                stepResult.addChildResult(forResult);
                doExecuteScriptStep(step.getComponentLib(), command, step, forResult);
                forFlag = (boolean) forResult.getKeywordReturn();
            }
            if (forFlag) { // 继续
                step.setChildSteps(ObjectUtil.cloneByStream(scriptStepMap.get(step.getId()).getChildSteps()));
                step.getArgs().put(KEY_FOR_CURRENT_INDEX, String.valueOf(++idx));
                setContextVariable(KEY_FOR_CURRENT_INDEX, Integer.valueOf(step.getArg(KEY_FOR_CURRENT_INDEX)));
            } else {    // 退出循环
                contextVars.remove(KEY_FOR_CURRENT_INDEX);
                step.getArgs().remove(KEY_FOR_CURRENT_INDEX);
            }
            return forFlag;
        }

        // if 逻辑
        if (stepResult.isAfterLogicExec()) {
            List<StepExecuteResult> childResultList = stepResult.getChildResult();
            // 已经执行过了childSteps内容
            switch (Objects.requireNonNull(logicStepType)) {   // command指令
                case _if:
                    if (LogicStepType._if_true.name().equals(ifStep) || LogicStepType._else.name().equals(ifStep)) {
                        // if已完成，退出if
                        return false;
                    }
                    if (LogicStepType._if_condition.name().equals(ifStep) || LogicStepType._else_if.name().equals(ifStep)) {
                        if (childResultList != null && !childResultList.isEmpty()) {
                            StepExecuteResult last = childResultList.get(childResultList.size() - 1);
                            if (LogicStepType._else_if.name().equals(ifStep)
                                    && last.getKeywordReturn() instanceof Boolean && (boolean) last.getKeywordReturn()) {
                                // else_if 的结果为true，else_if已经结束，退出if
                                return false;
                            }
                            stepResult.setKeywordReturn(last.getKeywordReturn());
                            step.getArgs().put("mode", "1");  // 设置成简单模式
                            stepResult.setAfterLogicExec(false); // 重新设置if为未执行
                        }
                    } else {
                        return false;   // if已经执行完成，退出if
                    }
                    // if_condition 和 else_if 后续还要跑具体
                    break;
                case _if_condition:
                    if (childResultList == null || childResultList.isEmpty()) {    // 获取最后一步的返回设置 _if_condition的返回keywordReturn
                        // 没有步骤结果则报错
                        throw new ExecuteException("condition没有可查看的步骤结果");
                    }
                    StepExecuteResult last = childResultList.get(childResultList.size() - 1);
                    if (last.getOutput().values().size() != 1) {
                        // 输出参数错误
                        throw new ExecuteException(String.format("步骤 %s 内必须有且只有一个输出参数，且为布尔值", step.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME)));
                    }
                    Object o = last.getOutput().values().toArray()[0];
                    if (null != o && ("true".equalsIgnoreCase(o.toString()) || "false".equalsIgnoreCase(o.toString()))) {
                        // 将输出参数设置为condition的返回keywordReturn
                        stepResult.setKeywordReturn(Boolean.parseBoolean(o.toString()));
                    } else {
                        throw new ExecuteException(String.format("步骤 %s 内的判断条件最后一步的输出参数无法转换为布尔值", step.getExtAttr(DebugerConst.ReqConst.KEY_COMMAND_NAME)));
                    }
                    return false;
                case _if_true:
                    // true内组件已经执行完，退出true
                    return false;
                case _else_if:
                    if (LogicStepType._if_true.name().equalsIgnoreCase(ifStep)) {   // else_if 已经执行完了
                        stepResult.setKeywordReturn(true);  // 用于if的判断，else_if已经行完
                        return false;
                    }
                    if (LogicStepType._if_condition.name().equalsIgnoreCase(ifStep)) {
                        StepExecuteResult conditionResult = stepResult.getChildResult().get(stepResult.getChildResult().size() - 1);
                        boolean elseIfCondition = (boolean) conditionResult.getKeywordReturn();
                        stepResult.setKeywordReturn(elseIfCondition);
                        if (!elseIfCondition) {
                            return false;   // else_if的条件结果不通过，返回继续if的
                        }
                    }
                    stepResult.setAfterLogicExec(false);
                    step.getArgs().put("mode", "1");  // 设置成简单模式
                    break;
                case _else:
                    // else内组件已经执行，退出else
                    return false;
                default:
                    // 默认返回false
                    return false;
            }
        }
        if(!stepResult.isAfterLogicExec()) {
            // 还没执行childSteps内容
            switch (Objects.requireNonNull(logicStepType)) {
                case _if:
                    Object ifReturn = stepResult.getKeywordReturn();
                    step.setChildSteps(new ArrayList<>());
                    if (!(ifReturn instanceof Boolean)) {   // if的结果是布尔值
                        // todo:
                        return false;
                    }
                    String ifMode = step.getArgs().get("mode");
                    if (StringUtils.isNotBlank(ifMode) && "2".equals(ifMode)) {
                        // 高级模式
                        step.setIfStep(LogicStepType._if_condition.name());
                        List<ScriptStep> ifConditionSteps = step.getIfStepMap().get(LogicStepType._if_condition.name());
                        if (ifConditionSteps == null || ifConditionSteps.isEmpty()) {
                            throw new ExecuteException("condition内没有可以执行的组件");
                        }
                        step.getChildSteps().addAll(ifConditionSteps);
                        return true;
                    } else {
                        // 都处理成简单模式
                        if ((boolean) ifReturn) {  // 判断if的结果
                            step.setIfStep(LogicStepType._if_true.name());
                            List<ScriptStep> trueSteps = step.getIfStepMap().get(LogicStepType._if_true.name());
                            step.getChildSteps().addAll(trueSteps);
                            return true;    // 进入
                        }
                        // 判断是否有else_if
                        if (step.getIfStepMap().containsKey(LogicStepType._else_if.name())
                                && !CollectionUtils.isEmpty(step.getIfStepMap().get(LogicStepType._else_if.name()))) {
                            step.setIfStep(LogicStepType._else_if.name());
                            ScriptStep elseIfStep = step.getIfStepMap().get(LogicStepType._else_if.name()).get(0);
                            step.getChildSteps().add(elseIfStep);
                            step.getIfStepMap().get(LogicStepType._else_if.name()).remove(elseIfStep);  // 可能有多个else_if，执行一次替换一次
                            return true; // 进入while循环
                        }
                        // 判断是否有else
                        if (step.getIfStepMap().containsKey(LogicStepType._else.name())
                                && !CollectionUtils.isEmpty(step.getIfStepMap().get(LogicStepType._else.name()))) {
                            step.setIfStep(LogicStepType._else.name());
                            List<ScriptStep> elseSteps = step.getIfStepMap().get(LogicStepType._else.name());
                            step.getChildSteps().addAll(elseSteps);
                            return true;    // 进入while循环
                        }
                    }
                    return false;   // 啥都没有，不进入while循环
                case _else_if:
                    Object elseIfReturn = stepResult.getKeywordReturn();
                    step.setChildSteps(new ArrayList<>());
                    if (!(elseIfReturn instanceof Boolean)) {
                        return false;
                    }
                    String elseMod = step.getArgs().get("mode");
                    if (StringUtils.isNotBlank(elseMod) && "2".equals(elseMod)) {
                        // todo: 高级模式
                        step.setIfStep(LogicStepType._if_condition.name());
                        List<ScriptStep> elseIfConditionSteps = step.getIfStepMap().get(LogicStepType._if_condition.name());
                        if (elseIfConditionSteps == null || elseIfConditionSteps.isEmpty()) {
                            throw new ExecuteException("condition内没有可以执行的组件");
                        }
                        step.getChildSteps().addAll(elseIfConditionSteps);
                        return true;
                    } else {
                        // todo: 统一处理成简单模式
                        if ((boolean) elseIfReturn) { // else_if的判断结果为true
                            step.setIfStep(LogicStepType._if_true.name());
                            List<ScriptStep> elseTrueSteps = step.getIfStepMap().get(LogicStepType._if_true.name());
                            step.getChildSteps().addAll(elseTrueSteps);
                            return true;    // 进入循环
                        } else {
                            step.setIfStep(LogicStepType._else_if.name());
                            return false;
                        }
                    }
                case _else:
                    // 直接进入循环
                    return true;
                default:
                    return false;
            }
        }
        return false;
    }

    /**
     * 判断该组件是否为逻辑组件，需要执行逻辑控制
     *
     * @param comLib 库名
     * @param keyword 关键字
     * @return true 是逻辑组件 | false 不是逻辑组件
     */
    private boolean isLogic(String comLib, String keyword) {
        if (StringUtils.isBlank(keyword)) {
            return false;
        }
        LogicStepType logicStepType = LogicStepType.get(keyword);
        return null != logicStepType;
    }

    private Object doExecuteScriptStep(String comLib, String keyword, ScriptStep step, StepExecuteResult stepRet) {
        setContextVariable(KEY_CURRENT_STEP, step);
        // 执行前时间
        long startTime = System.currentTimeMillis();
        try {
            // 获取库实例
            ComponentLibrary cmptLib = libraryInstanceMap.get(comLib);
            // 步骤执行日志
            stepLog = new StepLog(LogLevel.INFO.name(), currentStepTitle, currentStepType, keyword);
            stepLog.setCmptName(cmptLib.getKeywordAlias(keyword));
            // 执行前的日志、参数输出
            stepLogBeforeExecute(keyword, stepRet, cmptLib);
            // 对args里的参数进行处理
            extractVariables(step, cmptLib.getKeywordArgumentMap(keyword));
            if (LogicStepType._for.name().equalsIgnoreCase(keyword)) {
                if (step.getArg(KEY_FOR_CURRENT_INDEX) == null) {
                    step.getArgs().put(KEY_FOR_CURRENT_INDEX, String.valueOf(0));
                }
                setContextVariable(KEY_FOR_CURRENT_INDEX, Integer.valueOf(step.getArg(KEY_FOR_CURRENT_INDEX)));
            }
            //点击事件执行前截图
            boolean isClick = keyword.contains("click");
            /*if (isClick) {
                // 如果需要截图，执行截图操作
                snapshot(stepRet, snapshot, "步骤执行前截图");
            }*/
            // 执行关键字
            Object result = null;
            try {
                result = cmptLib.runKeyword(keyword, this);
            } catch (Exception e){
                if (e instanceof AssertException) {
                    log.info("是否截图参数：{}", snapshot);
                    log.info("keyword:{}", keyword);
                    if (keyword.contains("assertElement") && snapshot) {
                        snapshot(stepRet,snapshot,"断言失败截图");
                    }
                    throw new AssertException("断言失败，找不到目标元素");
                }
                throw e;
            }

            //非点击事件执行后截图
            if(!isClick) {
                // 步骤执行可能太快，导致截图不准确，加等待时间
                //Thread.sleep(1000);
                // 如果需要截图，执行截图操作
                snapshot(stepRet, snapshot, "步骤执行后截图");
            }
            if (isClick && snapshot) {
                log.debug("步骤执行时截图");
                log.debug("组件类型为：{}", cmptLib.getType());
                // 移动端点击类组件截图
                if (this.getVariableValue(DebugerConst.ReplyConst.MOBILE_STEP_SNAPSHOT) != null) {
                    String contentSnapshotPath = this.getVariableValue(DebugerConst.ReplyConst.MOBILE_STEP_SNAPSHOT);
                    log.debug("移动端组件截图，{}", contentSnapshotPath);
                    stepRet.setActualImgUri(contentSnapshotPath);
                }
                // pc端点击类组件截图
                if (cmptLib.getType() != null && cmptLib.getType() == LibraryConstant.LIB_TYPE_PCBS) {
                    try {
                        String stepSnapshot = String.valueOf(this.getVariableValue(DebugerConst.ReplyConst.PC_STEP_SNAPSHOT));
                        String tenantId = this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString();
                        log.debug("localPath = " + stepSnapshot);
                        log.debug("tenantId = " + tenantId);
                        if (StrUtil.isNotEmpty(tenantId) && StrUtil.isNotEmpty(stepSnapshot)){
                            Attachment upload = HttpUtils.upload(stepSnapshot, tenantId, currentTaskId, FileBusinessTypeEnum.PC_TASK_SCREENSHOT.getCode());
                            if (ObjectUtil.isNull(upload) || StringUtils.isBlank(upload.getId())) {
                                throw new RuntimeException("截图文件上传信息为空");
                            }
                            log.debug("截图文件上传结果，{}", JSON.toJSONString(upload));
                            stepRet.setActualImgUri(upload.getUrlPath());
                        }
                    } catch (Exception e) {
                        log.error("获取步骤执行时截图失败", e);
                    }
                }
            }
            //
            // 执行后日志输出与处理
            String outputMsg = stepLogAfterExecute(keyword, stepRet, cmptLib);

            if (result != null) {
                outputMsg = outputMsg + String.format("，方法返回值【%s】", JSON.toJSONString(result, true));
                stepRet.getOutput().put(DebugerConst.ReplyConst.KEY_KEYWORD_RETURN, result);
                stepRet.setKeywordReturn(result);
                // 截图处理
                snapshotCmptAfterExecute(keyword, stepRet, result);
            }
            log.debug(outputMsg);
            stepRet.setOutputMsg(outputMsg);
            basicCmptReply();
            assertCmptAfterExecute(keyword, stepRet, result);
            return result;
        }  catch (AssertException e) {
            stepRet.setSuccess(false);
            throw e;
        } catch (Exception e) {
            log.info("=========失败原因："+e.getMessage());
            //driver失效了，丢出去重新创建一个driver再执行一遍
            Throwable cause = e.getCause();
            if (cause instanceof NoSuchSessionException) {
                log.warn("driver失效了");
                NoSuchSessionException nsse = (NoSuchSessionException) cause;
                throw nsse;
            }
            snapshot(stepRet, errorSnapshot, "步骤执行失败截图");
            stepRet.setSuccess(false);
            // 如果是断言组件执行失败也算是断言失败 TODO: 之后根据需求再细化
            if (keyword.startsWith("assert")) {
                // 抛断言异常异常
                log.error("断言失败", e);
                String msg = "断言失败，实际结果与预期结果不符";
                doLog(LogLevel.ERROR, msg);
                throw new AssertException(msg);
            } else {
                String msg = "";
                if (e instanceof NullPointerException) {
                    msg = "执行失败：组件无法执行";
                } else {
                    msg = String.format("正在使用执行机：%s，执行失败：%s", EngineRuntime.getRuntime().getEngineInfo().getIpAddress() + "\n", e.getMessage()==null?"参数获取失败":e.getMessage());
                }
                doLog(LogLevel.ERROR, msg);
                throw new ExecuteException(String.format("组件库[%s]，关键字[%s]执行失败，可能原因：%s", comLib, keyword, e.getMessage()==null?"参数获取失败":e.getMessage()), e);
            }
        } finally {
            stepRet.setExecuted(true);
            stepRet.setExecuteTime(System.currentTimeMillis() - startTime);
        }
    }

    /**
     * 业务组件/接口调用内，基础组件执行后
     *
     */
    private void basicCmptReply() {
        if (null != parentContext) {
            HashMap<String, Object> map = new HashMap<>();
            map.put(DebugerConst.KEY_REQUESTID, requestId);
            map.put(DebugerConst.ReplyConst.KEY_SUCCESS, true);
            if (listener != null) {
                listener.doReply(this, DebugerConst.ReplyConst.KEY_CMD_REPLY, LogLevel.INFO.name(), map);
            }
        }
    }

    private void stepLogBeforeExecute(String keyword, StepExecuteResult stepRet, ComponentLibrary cmptLib) throws Exception {
        // 获取关键字的参数列表
        List<KeywordArgument> keywordArguments = cmptLib.getKeywordArguments(keyword);
        Map<String, Object> dataBus = getDataBusFromContext(this, keywordArguments);
        String inputMsg = String.format("正在使用执行机：%s，运行组件：库:【%s】,版本【%s】，组件【%s】，参数【%s】",
                EngineRuntime.getRuntime().getEngineInfo().getIpAddress() + "\n", cmptLib.getName(), cmptLib.getVersion(), keyword, getKeywordParams(dataBus, keywordArguments));
        // 输出日志 replay_log
        doLog(LogLevel.INFO, inputMsg);
        log.debug(" ------> {}组件执行前的参数列表 -> {}", keyword, JsonUtils.toJson(stepRet.getResult().get(DebugerConst.ReplyConst.KEY_VARIABLES)));
        //设置组件入参 任务计划用
        getKeywordParams2Map(dataBus, keywordArguments, stepRet);
    }

    /**
     * 组件执行后日志输出
     *
     * @param keyword
     * @param stepRet
     * @param cmptLib
     * @return
     */
    private String stepLogAfterExecute(String keyword, StepExecuteResult stepRet, ComponentLibrary cmptLib) {
        String outputMsg = String.format("正在使用执行机：%s，组件运行结果：库:【%s】，版本【%s】，组件【%s】，脚本变量【%s】",
                EngineRuntime.getRuntime().getEngineInfo().getIpAddress() + "\n", cmptLib.getName(), cmptLib.getVersion(), keyword,
//                JsonUtils.toJson(stepRet.getResult().get(DebugerConst.ReplyConst.KEY_VARIABLES)));
        JSON.toJSONString(stepRet.getResult().get(DebugerConst.ReplyConst.KEY_VARIABLES), true));
        if (!projectGlobalVars.isEmpty()) {
            outputMsg = String.format(outputMsg + ",全局变量【%s】", JSON.toJSONString(projectGlobalVars, true));
        }
        doLog(LogLevel.DEBUG, outputMsg);
        return outputMsg;
    }

    /**
     * 截图组件执行后的处理
     * @param keyword
     * @param stepRet
     * @param result
     */
    private void snapshotCmptAfterExecute(String keyword, StepExecuteResult stepRet, Object result) {
        stepRet.getResult().put(DebugerConst.ReplyConst.KEY_KEYWORD_RETURN, result);
        // 执行关键字
        if ("takeScreen".equalsIgnoreCase(keyword)) {
            // 上传截图
            log.debug("上传截图入参：result，{}",result);
            log.debug("上传截图入参：__tenantId，{}",this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString());
            log.debug("上传截图入参：currentTaskId，{}",currentTaskId);
            log.debug("上传截图入参：code，{}",FileBusinessTypeEnum.PC_TASK_SCREENSHOT.getCode());
            Attachment upload = HttpUtils.upload(result.toString(), this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString(), currentTaskId, FileBusinessTypeEnum.PC_TASK_SCREENSHOT.getCode());
            if (null != upload && StringUtils.isNotBlank(upload.getUrlPath())) {
                String uploadMsg = "截图成功";
                stepLog.setLogType(StepLog.LogType.IMAGE.name());
                stepLog.getResources().add(upload.getUrlPath());
                doLog(LogLevel.INFO, uploadMsg);
                stepLog.setLogType(StepLog.LogType.TEXT.name());
                stepLog.getResources().clear();
            }
        }
    }

    /**
     * 截图
     *
     * @param stepRet 步骤结果
     */
    private void snapshot(StepExecuteResult stepRet, boolean snapshotFlag, String msg) {
        if (snapshotFlag) {
            log.info(msg);
            try {
                DeviceConnection deviceConnection = (DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN);
                String tenantId = this.getContextVariable(IScriptRuntimeContext.KEY_TASK_TENANT).toString();
                String imageUrl = deviceConnection.snapshotAllScreen(tenantId, currentTaskId);
                stepRet.setActualImgUri(imageUrl);
            } catch (Exception ex) {
                log.error("截图失败：", ex);
            }
        }
    }

    /**
     * 断言组件执行后的处理
     *
     * @param keyword
     * @param stepRet
     * @param result
     */
    private void assertCmptAfterExecute(String keyword, StepExecuteResult stepRet, Object result) {
        // 如果时断言组件，判断result是否布尔值，抛出异常
        if (keyword.startsWith("assert") && result instanceof Boolean) {
            if ((boolean) result) {
                stepRet.setMsg("断言成功");
            } else {
                // 抛断言异常异常
                String assertMsg = "断言失败，实际结果与预期结果不符";
                throw new AssertException(assertMsg);
            }
        }
    }

    @Override
    public void reset(Script script) {
        clear();
        this.script = script;
        init(this.script);
    }

    @Override
    public void setRuntimeListener(ScriptRuntimeListener listener) {
        log.debug("Setting runtime listener");
        this.listener = listener;
    }

    private void doLog(String level, String msg) {
        if (listener != null) {
            listener.doLog(this, level, msg);
        }
    }

    private void doLog(LogLevel logLevel, String msg) {
        if (listener != null) {
            this.stepLog.setLevel(logLevel.name());
            this.stepLog.setLog(msg);
            this.stepLog.setStepId(this.currentStepId);
            listener.doLog(this, this.stepLog);
        }
    }

    private void doReply(StepExecuteResult result) {
        if (listener != null && null == parentContext) {
            listener.doReply(this, result);
        }
    }

    private void doReply(String cmd, String logLevel, Object obj) {
        if (listener != null && null == parentContext) {
            listener.doReply(this, cmd, logLevel, obj);
        }
    }

    /**
     * 从指定步骤回放
     *
     * @param script
     * @param scriptStepId
     * @return
     */
    @Override
    public ScriptExecuteResult runStep(Script script, String scriptStepId, String replayType) {
        return runStep(script, scriptStepId, false, replayType);
    }

    /**
     * 从指定步骤回放
     *
     * @param script
     * @param scriptStepId
     * @param oneStepFlag  true 单步回放 | false 从制定步骤开始回放
     * @return
     */
    @Override
    public ScriptExecuteResult runStep(Script script, String scriptStepId, boolean oneStepFlag, String replayType) {
        this.startStepId = scriptStepId;
        this.startFlag = false;
        this.singleStepRelay = oneStepFlag;
        if (this.singleStepRelay) {
            findStep(scriptStepId);
        }
        this.replayType = replayType;
        ScriptExecuteResult ret = new ScriptExecuteResult();   // 脚本执行结果
        ret.setStartTime(System.currentTimeMillis());
        // 执行步骤
        try {
            executeSteps(script, ret);
        } catch (Exception e) {
            throw e;
        } finally {
            ret.setEndTime(System.currentTimeMillis());
        }
        return ret;
    }

    private void findStep(String scriptStepId){
        ScriptStep replayStep = null;
        for (ScriptStep step : commands) {
            if (scriptStepId.equalsIgnoreCase(step.getId())){
                return;
            }
            if (CollectionUtils.isEmpty(step.getChildSteps())) {
                // 没有子步骤，不是if或者for
                continue;
            }
            replayStep = findChildStep(step, scriptStepId);
            if (replayStep != null){
                break;
            }
        }
        if (replayStep != null) {
            commands.clear();
            commands.add(replayStep);
        }
    }

    private ScriptStep findChildStep(ScriptStep step, String scriptStepId){
        for (ScriptStep childStep : step.getChildSteps()) {
            if (scriptStepId.equalsIgnoreCase(childStep.getId())){
                return childStep;
            }
            if (!CollectionUtils.isEmpty(childStep.getChildSteps())){
                ScriptStep grandStep = findChildStep(childStep, scriptStepId);
                if (grandStep != null){
                    return grandStep;
                }
            }
        }
        return null;
    }

    @Override
    public DebuggerRequest getCurrentRequest() {
        return (DebuggerRequest) getContextVariable(KEY_CURRENT_REQUEST);
    }

    @Override
    public boolean isSnapshot() {
        return snapshot;
    }

    @Override
    public boolean isRecord() {
        return record;
    }

    @Override
    public boolean isSaveMobileSyslog() {
        return saveSysLog;
    }

    /**
     * 获取变量
     * @param key
     */
    public Object getParameter(String key) {
        return null;
    }

    /**
     * 设置变量：脚本变量 | 全局变量
     * @param key ${a} | #{b}
     * @param value 具体值
     */
    public void setParameter(String key, Object value) {
        // 判断参数类型
        boolean scriptType = RegexUtils.containScriptVar(key);
        if (scriptType) {
        }
    }

    @Override
    public Variable getVariableInfo(String key) {
        return variableMap.get(key);
    }

    @Override
    public void setVariable(String key, String val) {
        variables.put(key, val);
    }

    /**
     *
     * @param key
     * @return
     */
    @Override
    public String getVariable(String key) {
        // 变量转换
        IScriptStep currentStep = getCurrentStep();
        String value = currentStep.getArgs().get(key);
        return variableReplace(value);
    }

    /**
     * 获取变量对应的值
     *
     * @param var
     * @return
     */
    @Override
    public String getVariableValue(String var) {
        return variables.get(var);
    }

    @Override
    public void setProjectGlobalVariable(String key, String val) {
        projectGlobalVars.put(key, val);
        // update the server side project global var
        updateGlobalVar(key, val);
    }

    /**
     * 更新服务端的全局变量
     * @param name
     * @param val
     */
    private void updateGlobalVar(String name, String val) {
        RestTemplate restTemplate = new RestTemplate();
        VariableDto updateDto = new VariableDto();
        updateDto.setVariableType(false);
        updateDto.setProjectId(projectId);
        updateDto.setValue(val);
        updateDto.setName(name);
        AtuServerConfig config = SpringUtils.getBean(AtuServerConfig.class);
        String url = config.getServerUrl() + config.getGlobalVarUpdatePath();
        try {
            log.debug("准备更新全局变量，请求内容：{}，", JsonUtils.toJson(updateDto));
            ResponseEntity<ResultWrapper> resEntity = restTemplate.postForEntity(url, updateDto, ResultWrapper.class);
            if (HttpStatus.OK == resEntity.getStatusCode()) {
                ResultWrapper wrapper = resEntity.getBody();
                if (null == wrapper || !wrapper.isSuccess()) {
                    throw new ExecuteException(String.format("更新全局变量[%s]失败", name));
                }
                log.debug("全局变量更新成功 name -> {} value -> {}", name, val);
            } else {
                throw new ExecuteException(String.format("请求异常 请求状态信息：%s", resEntity.getStatusCode()));
            }
        } catch (RestClientException e) {
            throw new ExecuteException("请求异常", e);
        } catch (ExecuteException e) {
            throw e;
        } catch (Exception e) {
            log.error("更新全局变量{}失败", name, e);
            throw e;
        }

    }

    /**
     * 通过全局变量名称获取值
     *
     * @param key
     * @return
     */
    @Override
    public String getProjectGlobalVariable(String key) {
        String value = getGlobalVarFromServer(false, key);
        if (value != null) {
            projectGlobalVars.put(key, value);
        }
        return projectGlobalVars.get(key);
    }

    @Override
    public <T> T getService(Class<T> aClass) {
        return applicationContext.getBean(aClass);
    }

    @Override
    public String[] getAllContextNames() {
        return contextVars.keySet().toArray(new String[contextVars.size()]);
    }

    @Override
    public Object getContextVariable(String key) {
        return contextVars.get(key);
    }

    @Override
    public void setContextVariable(String key, Object o) {
        contextVars.put(key, o);
    }

    @Override
    public IExecuteContext getParentContext() {
        return parentContext;
    }

    @Override
    public IScriptStep getCurrentStep() {
        return (ScriptStep) getContextVariable(KEY_CURRENT_STEP);
    }

    /**
     * 获取测试目标信息
     *
     * @return
     */
    @Override
    public ITestTarget[] getTestTarget() {
        return this.script.getTestTargets();
    }

    /**
     * 获取变量定义类型
     *
     * @param key    变量名称
     * @param global 是否全局变量
     * @return
     */
    @Override
    public String getVariableType(String key, boolean global) {
        if (global && globalVarmap.containsKey(key) && globalVarmap.get(key).getDataType() != null) {
            return globalVarmap.get(key).getDataType();
        } else if (!global && variableMap.containsKey(key) && variableMap.get(key).getType() != null) {
            return variableMap.get(key).getType();
        }
        return "string";
    }

    /**
     * 获取配置
     *
     * @param name 配置名称 例：spring.application.name
     * @return
     */
    @Override
    public String getProperty(String name) {
        return SpringUtils.getBean(Environment.class).getProperty(name);
    }

    /**
     * 获取配置，如果没有返回传入的默认值
     *
     * @param name         配置名称
     * @param defaultValue 默认值
     * @return
     */
    @Override
    public String getProperty(String name, String defaultValue) {
        return SpringUtils.getBean(Environment.class).getProperty(name, defaultValue);
    }

    /**
     * 获取设备信息
     *
     * @return
     */
    @Override
    public Map<String, Object> getDeviceInfo() {
        DeviceConnection deviceConnection = (DeviceConnection) this.getContextVariable(IScriptRuntimeContext.KEY_CURRENT_CONN);
        DeviceInfo deviceInfo = deviceConnection.getDeviceInfo();
        Map map = JSONObject.parseObject(JSONObject.toJSONString(deviceInfo), Map.class);
        return map;
    }

    /**
     * 执行表达式
     *
     * @param expression
     */
    @Override
    public Object evalExpression(String expression) {
        // 处理表达式所包含的变量
        return ArgumentUtil.evalExpression(this, expression);
    }

    private Map<String, Object> getDataBusFromContext(IExecuteContext context, List<KeywordArgument> arguments) throws KeywordNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Map<String, Object> dataBus = new HashMap<>();
        String name;
        for (KeywordArgument argument : arguments) {
            name = argument.getName();
            switch (argument.getScope()) {
                case ARGS: {
                    IScriptStep step = context.getCurrentStep();
                    dataBus.put(name, step.getArgs().get(name));
                    break;
                }
                case STEP: {
                    IScriptStep step = context.getCurrentStep();
                    Object value = step.getClass().getMethod("get" + StringUtils.capitalize(name)).invoke(step);
                    dataBus.put(name, value);
                    break;
                }
                case VARIABLES: {
                    dataBus.put(name, context.getVariable(name));
                    break;
                }
                case CONTEXT: {
                    dataBus.put(name, context.getContextVariable(name));
                    break;
                }
                case OUTPUT: {
                    IScriptStep step = context.getCurrentStep();
                    Map<String, String> map = step.getOutput();
                    dataBus.put(name, map.get(name));
                    break;
                }
            }
        }
        return dataBus;
    }

    public String getKeywordParams(Map<String, Object> dataBus, List<KeywordArgument> arguments) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append("{");
        int i = 1, size = arguments.size();
        for (KeywordArgument argument : arguments) {
            sb.append(argument.getComment()).append("(").append(argument.getName()).append(")=");
            sb.append(argument.isHideSensitive() ? "******" : object2String(dataBus.get(argument.getName())));
            if (i < size) {
                sb.append(", ");
            }
            i++;
        }
        sb.append("}");
        return sb.toString();
    }

    /**
     * 设置步骤
     *
     * @param dataBus
     * @param arguments
     * @param stepRet
     * @throws Exception
     */
    public void getKeywordParams2Map(Map<String, Object> dataBus, List<KeywordArgument> arguments, StepExecuteResult stepRet) throws Exception {
        Map<String, Object> paramMap = new HashMap<>();
        stepRet.setParamMap(paramMap);
        Map<String, Object> variables = (Map<String, Object>) stepRet.getResult().get("variables");
        log.debug("getKeywordParams2Map -> {}", JsonUtils.toJson(variables));
        for (KeywordArgument argument : arguments) {
            String value = object2String(dataBus.get(argument.getName()));
            if (RegexUtils.containScriptVar(value)) {
                String var = value.substring(2, value.length() - 1);
                log.debug("------> value ------> {}, var ----> {}", value, var);
                if (variables.containsKey(var) && null != variables.get(var)) {
                    value = var + "=" + variables.get(var).toString();
                }
            }
            paramMap.put(argument.getName(), value);
        }
    }

    private static String object2String(Object object) {
        if (object == null) {
            return "null";
        }
        return StringUtils.abbreviate(object.toString(), 500);
    }
}
