package expr

import (
	"regexp"
	"strings"

	"git.perx.ru/perxis/perxis-go/pkg/data"
	compiler2 "github.com/antonmedv/expr/compiler"
	"github.com/antonmedv/expr/parser"
	"github.com/antonmedv/expr/vm"
	"golang.org/x/net/context"
)

var (
	additionalFunctions = []string{"contains", "startsWith", "endsWith", "and", "or", "in", "not"}
	isExpression        = regexp.MustCompile(`[()}{<>=|&%*+\-\/\]\[\\]`).MatchString
)

const EnvContextKey = "$context"

func Eval(ctx context.Context, input string, env map[string]interface{}) (interface{}, error) {
	tree, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}

	e := GetEnv(ctx)

	if e == nil {
		e = make(map[string]interface{})
	}

	for k, v := range env {
		e[k] = v
	}

	e[EnvContextKey] = ctx
	cfg := GetDefaultConfig(e)

	env, _ = cfg.Env.(map[string]interface{})

	program, err := compiler2.Compile(tree, nil)
	if err != nil {
		return nil, err
	}

	output, err := vm.Run(program, env)
	if err != nil {
		return nil, err
	}

	return output, nil
}

func EvalKV(ctx context.Context, input string, kv ...interface{}) (interface{}, error) {
	m := make(map[string]interface{})

	for i := 0; i < len(kv)/2; i++ {
		key, ok := kv[i].(string)
		if !ok {
			panic("key should be string")
		}
		m[key] = kv[i+1]
	}

	return Eval(ctx, input, m)
}

func IsExpression(input string) bool {
	if isExpression(input) {
		return true
	}

	for _, s := range strings.Fields(input) {
		if data.Contains(s, additionalFunctions) {
			return true
		}
	}

	return false
}