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

// helpers.go
package expr

import (
	"fmt"
	"io"
	"os"
	"strings"

	"git.portale-stac.it/go-pkg/expr/kern"
	"git.portale-stac.it/go-pkg/expr/scan"
	"git.portale-stac.it/go-pkg/expr/types"
	"git.portale-stac.it/go-pkg/expr/types/float"
	"git.portale-stac.it/go-pkg/expr/util"
)

func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
	var tree *scan.Ast

	r := strings.NewReader(source)
	scanner := scan.NewScanner(r, scan.DefaultTranslations())
	parser := NewParser()

	if tree, err = parser.Parse(scanner); err == nil {
		result, err = tree.Eval(ctx)
	}
	return
}

type Arg struct {
	Name  string
	Value any
}

func EvalStringA(source string, args ...Arg) (result any, err error) {
	return EvalStringV(source, args)
}

func EvalStringV(source string, args []Arg) (result any, err error) {
	ctx := NewSimpleStoreWithoutGlobalContext()
	for _, arg := range args {
		if util.IsFunc(arg.Value) {
			if f, ok := arg.Value.(kern.FuncTemplate); ok {
				functor := kern.NewGolangFunctor(f)
				// ctx.RegisterFunc(arg.Name, functor, 0, -1)
				ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
					kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, 0),
				})
			} else {
				err = fmt.Errorf("invalid function specification: %q", arg.Name)
			}
		} else if integer, ok := types.AnyInteger(arg.Value); ok {
			ctx.SetVar(arg.Name, integer)
		} else if float, ok := float.AnyFloat(arg.Value); ok {
			ctx.SetVar(arg.Name, float)
		} else if _, ok := arg.Value.(string); ok {
			ctx.SetVar(arg.Name, arg.Value)
		} else if _, ok := arg.Value.(bool); ok {
			ctx.SetVar(arg.Name, arg.Value)
		} else {
			err = fmt.Errorf("unsupported type %T specified for item %q", arg.Value, arg.Name)
		}
	}

	if err == nil {
		result, err = EvalString(ctx, source)
	}
	return
}

func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
	var tree *scan.Ast
	scanner := scan.NewScanner(r, scan.DefaultTranslations())
	parser := NewParser()

	if tree, err = parser.Parse(scanner); err == nil {
		result, err = tree.Eval(ctx)
	}
	return
}

func EvalFile(ctx kern.ExprContext, filePath string) (result any, err error) {
	var fh *os.File
	if fh, err = os.Open(filePath); err != nil {
		return nil, err
	}
	defer fh.Close()
	result, err = EvalStream(ctx, fh)
	return
}
