package utils

import (
	"fmt"
	"hash/fnv"
	"math"
	"time"

	"github.com/go-logr/logr"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"k8s.io/apimachinery/pkg/util/errors"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func FnvHash(raw string) string {
	hash := fnv.New32()
	// fnv.New32().Write() never return error
	_, _ = hash.Write([]byte(raw))
	return fmt.Sprintf("%x", hash.Sum32())
}

func MaxDuration(a, b time.Duration) time.Duration {
	if a > b {
		return a
	}

	return b
}

// ReconcileErr return relate reconcile.Result and non error if err only contains RequeueAfterError.
// Otherwise return the origin err.
func ReconcileErr(logger logr.Logger, err error) (reconcile.Result, error) {
	// single error
	if requeueAfter, ok := err.(*RequeueAfterError); ok {
		if requeueAfter.Cause != nil {
			logger.Info("re-queue resource", "after", requeueAfter.RequeueAfter, "cause", requeueAfter.Cause.Error())
		}

		return reconcile.Result{Requeue: true, RequeueAfter: requeueAfter.RequeueAfter}, nil
	}

	// an error list is raised, recursively resolve each err
	if errList, ok := err.(errors.Aggregate); ok {
		minimumRequeue := time.Duration(math.MaxInt64)
		for _, errItem := range errList.Errors() {
			result, err2 := ReconcileErr(logger, errItem)
			if err2 != nil {
				return reconcile.Result{}, err
			}

			// use the minimum requeue interval requested
			if result.RequeueAfter < minimumRequeue {
				minimumRequeue = result.RequeueAfter
			}
		}

		return reconcile.Result{Requeue: true, RequeueAfter: minimumRequeue}, nil
	}
	return reconcile.Result{}, err
}

func GetPredicateForProviderTypes(providerTypes ...string) predicate.Funcs {
	return predicate.NewPredicateFuncs(func(o client.Object) bool {
		objPT := o.GetLabels()[cloudv1.ProviderTypeLabel]
		for _, t := range providerTypes {
			if objPT == t {
				return true
			}
		}
		return false
	})
}
