package builtin

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"time"

	"github.com/expr-lang/expr/internal/deref"
	"github.com/expr-lang/expr/vm/runtime"
)

var (
	Index map[string]int
	Names []string

	// MaxDepth limits the recursion depth for nested structures.
	MaxDepth      = 10000
	ErrorMaxDepth = errors.New("recursion depth exceeded")
)

func init() {
	Index = make(map[string]int)
	Names = make([]string, len(Builtins))
	for i, fn := range Builtins {
		Index[fn.Name] = i
		Names[i] = fn.Name
	}
}

var Builtins = []*Function{
	{
		Name:      "all",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) bool)),
	},
	{
		Name:      "none",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) bool)),
	},
	{
		Name:      "any",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) bool)),
	},
	{
		Name:      "one",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) bool)),
	},
	{
		Name:      "filter",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) []any)),
	},
	{
		Name:      "map",
		Predicate: true,
		Types:     types(new(func([]any, func(any) any) []any)),
	},
	{
		Name:      "find",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) any)),
	},
	{
		Name:      "findIndex",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) int)),
	},
	{
		Name:      "findLast",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) any)),
	},
	{
		Name:      "findLastIndex",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) int)),
	},
	{
		Name:      "count",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) int)),
	},
	{
		Name:      "sum",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool) int)),
	},
	{
		Name:      "groupBy",
		Predicate: true,
		Types:     types(new(func([]any, func(any) any) map[any][]any)),
	},
	{
		Name:      "sortBy",
		Predicate: true,
		Types:     types(new(func([]any, func(any) bool, string) []any)),
	},
	{
		Name:      "reduce",
		Predicate: true,
		Types:     types(new(func([]any, func(any, any) any, any) any)),
	},
	{
		Name: "len",
		Fast: Len,
		Validate: func(args []reflect.Type) (reflect.Type, error) {
			if len(args) != 1 {
				return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
			}
			switch kind(args[0]) {
			case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface:
				return integerType, nil
37:		Name:      "all",
42:		Name:      "none",
47:		Name:      "any",
52:		Name:      "one",
57:		Name:      "filter",
62:		Name:      "map",
67:		Name:      "find",
72:		Name:      "findIndex",
77:		Name:      "findLast",
82:		Name:      "findLastIndex",
87:		Name:      "count",
92:		Name:      "sum",
97:		Name:      "groupBy",
102:		Name:      "sortBy",
107:		Name:      "reduce",
112:		Name: "len",
126:		Name:  "type",
131:		Name: "abs",
145:		Name: "ceil",
152:		Name: "floor",
159:		Name: "round",
166:		Name: "int",
184:		Name: "float",
202:		Name:  "string",
207:		Name: "trim",
223:		Name: "trimPrefix",
237:		Name: "trimSuffix",
251:		Name: "upper",
258:		Name: "lower",
265:		Name: "split",
281:		Name: "splitAfter",
297:		Name: "replace",
313:		Name: "repeat",
328:		Name: "join",
355:		Name: "indexOf",
362:		Name: "lastIndexOf",
369:		Name: "hasPrefix",
376:		Name: "hasSuffix",
383:		Name: "max",
392:		Name: "min",
401:		Name: "mean",
417:		Name: "median",
437:		Name: "toJSON",
448:		Name: "fromJSON",
460:		Name: "toBase64",
467:		Name: "fromBase64",
478:		Name: "now",
506:		Name: "duration",
513:		Name: "date",
586:		Name: "timezone",
597:		Name: "first",
620:		Name: "last",
643:		Name: "get",
647:		Name: "take",
686:		Name: "keys",
716:		Name: "values",
746:		Name: "toPairs",
774:		Name: "fromPairs",
810:		Name: "reverse",
845:		Name: "uniq",
