package cluster

import (
	"context"
	"fmt"
	"testing"

	. "github.com/onsi/gomega"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/tidbcloud/infra-api/controllers/utils"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"

	"github.com/tidbcloud/infra-api/controllers/test"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

func TestWhenPhaseIn(t *testing.T) {
	cases := map[string]struct {
		filter  whenPhaseIn
		cluster *cloudv1.Cluster
		allow   bool
	}{
		"AllowPhase": {
			filter: whenPhaseIn([]cloudv1.ClusterPhase{cloudv1.ClusterPhaseRunning}),
			cluster: &cloudv1.Cluster{
				Status: cloudv1.ClusterStatus{
					Phase: cloudv1.ClusterPhaseRunning,
				},
			},
			allow: true,
		},
		"DenyPhase": {
			filter: whenPhaseIn([]cloudv1.ClusterPhase{cloudv1.ClusterPhaseRunning}),
			cluster: &cloudv1.Cluster{
				Status: cloudv1.ClusterStatus{
					Phase: cloudv1.ClusterPhaseUpgrading,
				},
			},
			allow: false,
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			g.Expect(tc.filter.Allow(tc.cluster)).To(Equal(tc.allow))
		})
	}
}

func TestMutateTidbCluster(t *testing.T) {
	version501 := "v5.0.1"
	cases := map[string]struct {
		tc       *v1alpha1.TidbCluster
		mutateFn func(*v1alpha1.TidbCluster) error
		mutated  bool
		err      bool
		dryrun   bool
	}{
		"Update": {
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{
					Version: "v5.0.0",
				},
			},
			mutateFn: func(tc *v1alpha1.TidbCluster) error {
				tc.Spec.Version = version501
				return nil
			},
			mutated: true,
			err:     false,
		},
		"NoUpdate": {
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{
					Version: version501,
				},
			},
			mutateFn: func(tc *v1alpha1.TidbCluster) error {
				tc.Spec.Version = version501
				return nil
			},
			mutated: false,
			err:     false,
		},
		"MutateError": {
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{
					Version: version501,
				},
			},
			mutateFn: func(tc *v1alpha1.TidbCluster) error {
				return fmt.Errorf("error mutating")
			},
			mutated: false,
			err:     true,
		},
		"Dryrun": {
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{
					Version: "v5.0.0",
				},
			},
			mutateFn: func(tc *v1alpha1.TidbCluster) error {
				tc.Spec.Version = version501
				return nil
			},
			mutated: false,
			err:     false,
			dryrun:  true,
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			shootClient := test.NewMockClient()
			shootClient.MockUpdate = func(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
				return nil
			}
			target := &cloudv1.Cluster{}
			if tc.dryrun {
				target.SetAnnotations(map[string]string{
					utils.DryrunAnnotation: "true",
				})
			}
			r := &reconcileContext{
				shootClient: shootClient,
				tc:          tc.tc,
				Log:         log.NullLogger{},
			}
			r.Target = target
			r.GitOps, r.Cluster = r, r
			mutated, err := r.mutateTidbCluster(tc.mutateFn)
			g.Expect(mutated).To(Equal(tc.mutated))
			if tc.err {
				g.Expect(err).NotTo(Succeed())
			} else {
				g.Expect(err).To(Succeed())
			}
		})
	}
}
