package net.northking.cctp.se.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import lombok.extern.slf4j.Slf4j;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.keyword.KeywordArgument;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class ArgumentUtil {

    private static ScriptEngine engine = new GroovyScriptEngineImpl();

    /**
     * 处理组件参数值
     * todo: 可删掉
     */
    public static String handle(KeywordArgument argument, IExecuteContext context, String text) {
        Set<String> globalVar = RegexUtils.extractGlobalVar(text);
        Set<String> scriptVar = RegexUtils.extractScriptVar(text);
        String value;
        if (!argument.isExpression()) {
            for (String var : scriptVar) {
                value = context.getVariableValue(var);
                text = StringUtils.replace(text, "${" + var + "}", value);
            }
            for (String var : globalVar) {
                value = context.getProjectGlobalVariable(var);
                text = StringUtils.replace(text, "${" + var + "}", value);
            }
            return text;
        }
        return text;
    }

    /**
     * 执行表达式参数，形如：${var1} + "a" + #{g_var1}
     */
    public static Object evalExpression(IExecuteContext context, String text) {
        Set<String> globalVar = RegexUtils.extractGlobalVar(text);
        Set<String> scriptVar = RegexUtils.extractScriptVar(text);
        String expression = text;
        String value;
        log.debug("开始处理表达式：{}", expression);
        for (String var : scriptVar) {
            value = context.getVariableValue(var);
            if (value == null) {
                value = var;
            }
            Object restoreVar = restoreVarType(context, var, value, false);
            log.debug("表达式处理前：{}",expression);
            expression = StringUtils.replace(expression, "${" + var + "}", JSON.toJSONString(restoreVar));
        }
        for (String var : globalVar) {
            value = context.getProjectGlobalVariable(var);
            if (value == null) {
                value = var;
            }
            Object restoreVar = restoreVarType(context, var, value, true);
            log.debug("表达式处理前：{}",expression);
            expression = StringUtils.replace(expression, "#{" + var + "}", JSON.toJSONString(restoreVar));
        }
        log.debug("表达式处理后：{}", expression);
        Map<String, Object> vars = new HashMap<>();
        try {
            return evalExpression(vars, expression);
        } catch (Exception e) {
            throw new ExecuteException("表达式执行失败：" + text);
        }
    }

    public static Object evalExpression(Map<String, Object> vars, String expression) throws Exception {
        try {
            ExpressionSimpleBindings bindings = new ExpressionSimpleBindings(vars);
            engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
            Object result = engine.eval(expression);
            return result;
        } catch (Exception e) {
            log.error("表达式错误，可能存在未声明的变量：" + expression, e);
            throw e;
        }
    }

    private static Object restoreVarType(IExecuteContext context, String var, String value, boolean isGlobal) {
        String restoreVar = context.getVariableType(var, isGlobal);
        switch (restoreVar) {
            case "boolean":
                return Boolean.parseBoolean(value);
            case "number":
                return new BigDecimal(value);
            case "object":
                return JSON.parse(value);
            case "array":
                return JSONArray.parse(value);
            default:
                return value;
        }
    }

    public static class ExpressionSimpleBindings implements Bindings {

        private Map<String, Object> map;

        public ExpressionSimpleBindings(Map<String, Object> map) {
            this.map = map;
        }

        @Override
        public Object put(String name, Object value) {
            return map.put(name, value);
        }

        @Override
        public void putAll(Map<? extends String, ?> toMerge) {
            map.putAll(toMerge);
        }

        @Override
        public void clear() {
            map.clear();
        }

        @Override
        public Set<String> keySet() {
            return map.keySet();
        }

        @Override
        public Collection<Object> values() {
            return map.values();
        }

        @Override
        public Set<Entry<String, Object>> entrySet() {
            return map.entrySet();
        }

        @Override
        public int size() {
            return map.size();
        }

        @Override
        public boolean isEmpty() {
            return map.isEmpty();
        }

        @Override
        public boolean containsKey(Object key) {
            return map.containsKey(key);
        }

        @Override
        public boolean containsValue(Object value) {
            return map.containsValue(value);
        }

        public Object get(Object key) {
            return map.get(key);
        }

        @Override
        public Object remove(Object key) {
            return map.remove(key);
        }
    }

}
