package common

import (
	"fmt"
	"testing"
	"time"

	. "github.com/onsi/gomega"
	"go.uber.org/multierr"
	"sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"

	reconcileerr "github.com/tidbcloud/infra-api/controllers/common/error"
)

func TestReconcileErr(t *testing.T) {
	unexpected := fmt.Errorf("something unexpected")

	var allRequeue error
	allRequeue = multierr.Append(allRequeue, &reconcileerr.RequeueAfterError{RequeueAfter: 30 * time.Second})
	allRequeue = multierr.Append(allRequeue, &reconcileerr.RequeueAfterError{RequeueAfter: 15 * time.Second})
	allRequeue = multierr.Append(allRequeue, &reconcileerr.RequeueAfterError{RequeueAfter: 5 * time.Second})

	var nested error
	nested = multierr.Append(nested, &reconcileerr.RequeueAfterError{RequeueAfter: 60 * time.Second})
	nested = multierr.Append(nested, allRequeue)

	var containUnexpected error
	containUnexpected = multierr.Append(containUnexpected, unexpected)
	containUnexpected = multierr.Append(containUnexpected, &reconcileerr.RequeueAfterError{RequeueAfter: 30 * time.Second})

	cases := map[string]struct {
		input  error
		result reconcile.Result
		err    error
	}{
		"Requeue": {
			input: &reconcileerr.RequeueAfterError{RequeueAfter: 1 * time.Minute},
			result: reconcile.Result{
				Requeue:      true,
				RequeueAfter: 1 * time.Minute,
			},
		},
		"UnexpectedError": {
			input:  unexpected,
			result: reconcile.Result{},
			err:    unexpected,
		},
		"AggregatedUseMinimumRequeueInterval": {
			input: allRequeue,
			result: reconcile.Result{
				Requeue:      true,
				RequeueAfter: 5 * time.Second,
			},
		},
		"NestedAggregation": {
			input: nested,
			result: reconcile.Result{
				Requeue:      true,
				RequeueAfter: 5 * time.Second,
			},
		},
		"AggregationContainsUnexpected": {
			input:  containUnexpected,
			result: reconcile.Result{},
			err:    unexpected,
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			result, err := ReconcileErr(&log.NullLogger{}, tc.input)
			g.Expect(result).To(Equal(tc.result))
			if tc.err == nil {
				g.Expect(err).To(BeNil())
			} else {
				g.Expect(err).To(Equal(tc.err))
			}
		})
	}
}
