// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// t_common_test.go
package expr

import (
	"errors"
	"reflect"
	"strings"
	"testing"
)

type inputType struct {
	source     string
	wantResult any
	wantErr    any
}

func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
	succeeded := 0
	failed := 0
	for _, count := range spec {
		good := doTest(t, ctx, section, &inputs[count-1], count)

		if good {
			succeeded++
		} else {
			failed++
		}
	}
	t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
}

func runTestSuiteSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
	runCtxTestSuiteSpec(t, nil, section, inputs, spec...)
}

func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inputType) {

	succeeded := 0
	failed := 0

	for i, input := range inputs {
		// fmt.Printf("%3d: %s\n", i+1, input.source)

		good := doTest(t, ctx, section, &input, i+1)
		if good {
			succeeded++
		} else {
			failed++
		}
	}
	t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
}
func runTestSuite(t *testing.T, section string, inputs []inputType) {
	runCtxTestSuite(t, nil, section, inputs)
}

func getWantedError(input *inputType) error {
	var wantErr error
	var ok bool
	if wantErr, ok = input.wantErr.(error); !ok {
		if msg, ok := input.wantErr.(string); ok {
			wantErr = errors.New(msg)
		}
	}
	return wantErr
}

func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
	var expr Expr
	var gotResult any
	var gotErr error

	wantErr := getWantedError(input)

	parser := NewParser()
	if ctx == nil {
		ctx = NewSimpleStore()
	}

	logTest(t, count, section, input.source, input.wantResult, wantErr)

	r := strings.NewReader(input.source)
	scanner := NewScanner(r, DefaultTranslations())

	good = true
	if expr, gotErr = parser.Parse(scanner); gotErr == nil {
		gotResult, gotErr = expr.Eval(ctx)
	}

	eq := reflect.DeepEqual(gotResult, input.wantResult)

	if !eq /*gotResult != input.wantResult*/ {
		t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
		good = false
	}

	if gotErr != wantErr {
		if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
			t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
			good = false
		}
	}
	return
}

func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
	if wantErr == nil {
		t.Logf("[+]%s nr %3d -- `%s`  --> %v", section, n, source, wantResult)
	} else {
		t.Logf("[-]%s nr %3d -- `%s`  --> %v", section, n, source, wantErr)
	}
}