package gval

import (
	"context"
	"testing"
)

func BenchmarkGval(bench *testing.B) {
	benchmarks := []evaluationTest{
		{
			// Serves as a "water test" to give an idea of the general overhead
			name:       "const",
			expression: "1",
		},
		{
			name:       "single parameter",
			expression: "requests_made",
			parameter: map[string]interface{}{
				"requests_made": 99.0,
			},
		},
		{
			name:       "parameter",
			expression: "requests_made > requests_succeeded",
			parameter: map[string]interface{}{
				"requests_made":      99.0,
				"requests_succeeded": 90.0,
			},
		},
		{
			// The most common use case, a single variable, modified slightly, compared to a constant.
			// This is the "expected" use case.
			name:       "common",
			expression: "(requests_made * requests_succeeded / 100) >= 90",
			parameter: map[string]interface{}{
				"requests_made":      99.0,
				"requests_succeeded": 90.0,
			},
		},
		{
			// All major possibilities in one expression.
			name: "complex",
			expression: `2 > 1 &&
			"something" != "nothing" ||
			date("2014-01-20") < date("Wed Jul  8 23:07:35 MDT 2015") && 
			object["Variable name with spaces"] <= array[0] &&
			modifierTest + 1000 / 2 > (80 * 100 % 2)`,
			parameter: map[string]interface{}{
				"object":       map[string]interface{}{"Variable name with spaces": 10.},
				"array":        []interface{}{0.},
				"modifierTest": 7.3,
			},
		},
		{
			// no variables, no modifiers
			name:       "literal",
			expression: "(2) > (1)",
		},
		{
			name:       "modifier",
			expression: "(2) + (2) == (4)",
		},
		{
			//   Benchmarks uncompiled parameter regex operators, which are the most expensive of the lot.
			//   Note that regex compilation times are unpredictable and wily things. The regex engine has a lot of edge cases
			//   and possible performance pitfalls. This test doesn't aim to be comprehensive against all possible regex scenarios,
			//   it is primarily concerned with tracking how much longer it takes to compile a regex at evaluation-time than during parse-time.
			name:       "regex",
			expression: "(foo !~ bar) && (foo + bar =~ oba)",
			parameter: map[string]interface{}{
				"foo": "foo",
				"bar": "bar",
				"baz": "baz",
				"oba": ".*oba.*",
			},
		},
		{
			// Benchmarks pre-compilable regex patterns. Meant to serve as a sanity check that constant strings used as regex patterns
			// are actually being precompiled.
			// Also demonstrates that (generally) compiling a regex at evaluation-time takes an order of magnitude more time than pre-compiling.
			name:       "constant regex",
			expression: `(foo !~ "[bB]az") && (bar =~ "[bB]ar")`,
			parameter: map[string]interface{}{
				"foo": "foo",
				"bar": "bar",
				"baz": "baz",
				"oba": ".*oba.*",
			},
		},
		{
			name:       "accessors",
			expression: "foo.Int",
			parameter:  fooFailureParameters,
		},
		{
			name:       "accessors method",
			expression: "foo.Func()",
			parameter:  fooFailureParameters,
		},
		{
			name:       "accessors method parameter",
			expression: `foo.FuncArgStr("bonk")`,
			parameter:  fooFailureParameters,
		},
		{
			name:       "nested accessors",
			expression: `foo.Nested.Funk`,
			parameter:  fooFailureParameters,
		},
		{
			name:       "decimal arithmetic",
			expression: "(requests_made * requests_succeeded / 100)",
			extension:  decimalArithmetic,
			parameter: map[string]interface{}{
				"requests_made":      99.0,
				"requests_succeeded": 90.0,
			},
		},
		{
			name:       "decimal logic",
			expression: "(requests_made * requests_succeeded / 100) >= 90",
			extension:  decimalArithmetic,
			parameter: map[string]interface{}{
				"requests_made":      99.0,
				"requests_succeeded": 90.0,
			},
		},
	}
	for _, benchmark := range benchmarks {
		eval, err := Full().NewEvaluable(benchmark.expression)
		if err != nil {
			bench.Fatal(err)
		}
		_, err = eval(context.Background(), benchmark.parameter)
		if err != nil {
			bench.Fatal(err)
		}
		bench.Run(benchmark.name+"_evaluation", func(bench *testing.B) {
			for i := 0; i < bench.N; i++ {
				eval(context.Background(), benchmark.parameter)
			}
		})
		bench.Run(benchmark.name+"_parsing", func(bench *testing.B) {
			for i := 0; i < bench.N; i++ {
				Full().NewEvaluable(benchmark.expression)
			}
		})

	}
}
