package customdiff

import (
	"context"

	"github.com/hashicorp/go-multierror"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// All returns a CustomizeDiffFunc that runs all of the given
// CustomizeDiffFuncs and returns all of the errors produced.
//
// If one function produces an error, functions after it are still run.
// If this is not desirable, use function Sequence instead.
//
// If multiple functions returns errors, the result is a multierror.
//
// For example:
//
//	&schema.Resource{
//	    // ...
//	    CustomizeDiff: customdiff.All(
//	        customdiff.ValidateChange("size", func (old, new, meta interface{}) error {
//	            // If we are increasing "size" then the new value must be
//	            // a multiple of the old value.
//	            if new.(int) <= old.(int) {
//	                return nil
//	            }
//	            if (new.(int) % old.(int)) != 0 {
//	                return fmt.Errorf("new size value must be an integer multiple of old value %d", old.(int))
//	            }
//	            return nil
//	        }),
//	        customdiff.ForceNewIfChange("size", func (old, new, meta interface{}) bool {
//	            // "size" can only increase in-place, so we must create a new resource
//	            // if it is decreased.
//	            return new.(int) < old.(int)
//	        }),
//	        customdiff.ComputedIf("version_id", func (d *schema.ResourceDiff, meta interface{}) bool {
//	            // Any change to "content" causes a new "version_id" to be allocated.
//	            return d.HasChange("content")
//	        }),
//	    ),
//	}
func All(funcs ...schema.CustomizeDiffFunc) schema.CustomizeDiffFunc {
	return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
		var err error
		for _, f := range funcs {
			thisErr := f(ctx, d, meta)
			if thisErr != nil {
				err = multierror.Append(err, thisErr)
			}
		}
		return err
	}
}

// Sequence returns a CustomizeDiffFunc that runs all of the given
// CustomizeDiffFuncs in sequence, stopping at the first one that returns
// an error and returning that error.
//
// If all functions succeed, the combined function also succeeds.
func Sequence(funcs ...schema.CustomizeDiffFunc) schema.CustomizeDiffFunc {
	return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
		for _, f := range funcs {
			err := f(ctx, d, meta)
			if err != nil {
				return err
			}
		}
		return nil
	}
}
