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

// t_funcs_test.go
package expr

import (
	"testing"

	"git.portale-stac.it/go-pkg/expr/kern"
)

func TestFuncs(t *testing.T) {
	section := "Funcs"
	inputs := []inputType{
		/*   1 */ {`two=func(){2}; two()`, int64(2), nil},
		/*   2 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil},
		/*   3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
		/*   4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
		/*   5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
		/*   6 */ {`@x="hello"; @x`, nil, `[1:3] variable references are not allowed in top level expressions: "@x"`},
		/*   7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
		/*   8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
		/*   9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, `undefined variable or function "x"`},
		/*  10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
		/*  11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
		/*  12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
		/*  13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
		/*  14 */ {`two=func(){2}; two(123)`, nil, `two(): too many params -- expected 0, got 1`},
		/*  15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
		/*  16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
		/*  17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
		/*  18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
		/*  19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
		/*  20 */ {`f=func(a,b){a*2+b}; f(1,10)`, int64(12), nil},
		/*  21 */ {`f=func(a,b){a*2+b}; f(a=2,b=1)`, int64(5), nil},
		/*  22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
		/*  23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
		/*  24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
		/*  25 */ {`f=func(x,x){x}`, nil, `[1:11] parameter "x" at position 2 already defined at position 1`},
		//		/*  20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
		//		/*  20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
		// /*  18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
	}

	// t.Setenv("EXPR_PATH", ".")

	//runTestSuiteSpec(t, section, inputs, 19)
	RunTestSuite(t, section, inputs)
}

func TestFuncs2(t *testing.T) {
	section := "Funcs2"
	inputs := []inputType{
		/*   1 */ {`sum=func(a,b){a+b}; sum(1,2)`, int64(3), nil},
		/*   2 */ {`sum=func(a,b){a+b}; sum(1)`, nil, `sum(): too few params -- expected 2, got 1`},
		/*   3 */ {`builtin "iterator"; times=func(a,b){a*b}; run($(["1", "2", "3"]), times)`, nil, `operator(): missing params -- a, b`},
	}
	// runTestSuiteSpec(t, section, inputs, 4)
	RunTestSuite(t, section, inputs)
}

func dummy(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
	return
}

func TestFunctionToStringSimple(t *testing.T) {
	source := kern.NewGolangFunctor(dummy)
	want := "func(){}"
	got := source.ToString(0)
	if got != want {
		t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
	}
}

func TestFunctionGetFunc(t *testing.T) {
	source := kern.NewGolangFunctor(dummy)
	want := kern.ExprFunc(nil)
	got := source.GetFunc()
	if got != want {
		t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
	}
}

func TestGoFunction(t *testing.T) {
	section := "Funcs"
	inputs := []inputType{
		/*   1 */ {`myName()`, "Celestino Amoroso", nil},
		/*   2 */ {`myName("Peppino")`, "Peppino", nil},
	}

	myName := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
		var ok bool
		if result, ok = args["name"].(string); !ok {
			err = kern.ErrWrongParamType(name, "name", kern.TypeString, args["name"])
		}
		return
	}

	ctx := NewSimpleStoreWithoutGlobalContext()
	ctx.RegisterFunc("myName", kern.NewGolangFunctor(myName), kern.TypeString, []kern.ExprFuncParam{
		kern.NewFuncParamFlagDef("name", kern.PfOptional|kern.PfDefault, "Celestino Amoroso"),
	})

	RunCtxTestSuite(t, ctx, section, inputs)

}
