package objects

import (
	"context"

	"github.com/MontFerret/ferret/pkg/runtime/core"
	"github.com/MontFerret/ferret/pkg/runtime/values"
	"github.com/MontFerret/ferret/pkg/runtime/values/types"
)

// MergeRecursive recursively merge the given objects into a single object.
// @params objs (Objects) - objects to merge.
// @returns (Object) - Object created by merging.
func MergeRecursive(_ context.Context, args ...core.Value) (core.Value, error) {
	err := core.ValidateArgs(args, 1, core.MaxArgs)
	if err != nil {
		return values.None, err
	}

	for _, arg := range args {
		if err = core.ValidateType(arg, types.Object); err != nil {
			return values.None, err
		}
	}

	merged := values.NewObject()

	for _, arg := range args {
		merged = merge(merged, arg).(*values.Object)
	}

	return merged.Clone(), nil
}

func merge(src, dst core.Value) core.Value {
	if src.Type() != dst.Type() {
		return dst
	}

	if src.Type() != types.Object {
		return dst
	}

	srcObj := src.(*values.Object)
	dstObj := dst.(*values.Object)

	if dstObj.Length() == 0 {
		return src
	}

	keyObj := values.NewString("")
	exists := values.NewBoolean(false)
	var srcVal core.Value

	dstObj.ForEach(func(val core.Value, key string) bool {
		keyObj = values.NewString(key)

		if srcVal, exists = srcObj.Get(keyObj); exists {
			val = merge(srcVal, val)
		}

		srcObj.Set(keyObj, val)
		return true
	})

	return src
}
