package common

import (
	"context"
	"testing"

	"github.com/go-logr/logr"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpc/task"
	resvpc "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpc"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockvpc"
	"go.uber.org/mock/gomock"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestGuardian_Create(t *testing.T) {
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	vpc := &v1alpha1.VPC{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test",
			Namespace: "default",
		},
	}
	cli := fake.NewClientBuilder().WithScheme(s).WithObjects(vpc).WithStatusSubresource(vpc).Build()

	c := &task.Context{
		Local:  k8s.NewMutClientWithOwner(cli, vpc.DeepCopy(), namer.NewNamer()).Client(),
		Logger: logr.Discard(),
		VPC:    vpc,
		VPCRW:  resvpc.NewVPCReaderWriter(cli),
		Config: config.RootConfig{},
	}
	gu := NewGuardian(c)
	t.Run("AddFinalizer", func(t *testing.T) {
		assert.Nil(t, vpc.GetFinalizers())
		result := gu.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, ctrl.Result{Requeue: true, RequeueAfter: 0}, res)
		assert.Equal(t, []string{v1alpha1.Finalizer}, vpc.GetFinalizers())
	})
	t.Run("Set vpc phase to VPCPhaseCreating", func(t *testing.T) {
		result := gu.Create(context.Background())
		assert.True(t, result.IsDoneState())
		assert.Equal(t, v1alpha1.VPCPhaseCreating, vpc.Status.Phase)

		casts := []struct {
			phase v1alpha1.VPCPhase
			done  bool
		}{
			{phase: v1alpha1.VPCPhasePending, done: true},
			{phase: v1alpha1.VPCPhaseReadyForCreate, done: true},
			{phase: v1alpha1.VPCPhaseFailed, done: true},
			{phase: v1alpha1.VPCPhaseCreating, done: true},
		}
		for _, cast := range casts {
			t.Run(string(cast.phase), func(t *testing.T) {
				vpc.Status.Phase = cast.phase
				gu.VPC.Finalizers = []string{v1alpha1.Finalizer}
				result := gu.Create(context.Background())
				assert.Equal(t, cast.done, result.IsDoneState())
				assert.Equal(t, v1alpha1.VPCPhaseCreating, vpc.Status.Phase)
			})
		}
	})

	t.Run("UpdateStatus error", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockvpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
		c.VPCRW = mockvpcrw
		gu := NewGuardian(c)
		mockvpcrw.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		gu.VPC.Finalizers = []string{v1alpha1.Finalizer}
		result := gu.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, ctrl.Result{Requeue: true, RequeueAfter: 0}, res)
		assert.Equal(t, []string{v1alpha1.Finalizer}, vpc.GetFinalizers())
		assert.Equal(t, v1alpha1.VPCPhaseCreating, vpc.Status.Phase)
	})
}
