package common

import (
	"context"
	"testing"

	"github.com/go-logr/logr"
	"github.com/stretchr/testify/assert"
	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"

	"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/key/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/k8s"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/namer"
)

func TestGuardian_Create(t *testing.T) {
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	key := &v1alpha1.Key{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test",
			Namespace: "default",
		},
		Spec: v1alpha1.KeySpec{
			Provider: "eks",
			AccountRef: v1alpha1.AccountReference{
				ProviderConfigName: "test-config",
				AccountID:          "123456789",
			},
		},
	}
	cli := fake.NewClientBuilder().WithScheme(s).WithObjects(key).WithStatusSubresource(key).Build()

	c := &task.Context{
		Local:  k8s.NewMutClientWithOwner(cli, key.DeepCopy(), namer.NewNamer()).Client(),
		Logger: logr.Discard(),
		Key:    key,
		Config: config.RootConfig{},
	}
	gu := NewGuardian(c)

	t.Run("AddFinalizer", func(t *testing.T) {
		assert.Nil(t, key.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}, key.GetFinalizers())
	})

	t.Run("Set key phase to KeyPhaseCreating", func(t *testing.T) {
		result := gu.Create(context.Background())
		assert.True(t, result.IsDoneState())
		assert.Equal(t, v1alpha1.KeyPhaseCreating, key.Status.Phase)

		// Test different initial phases
		testCases := []struct {
			phase v1alpha1.KeyPhase
			done  bool
		}{
			{phase: "", done: true},
			{phase: v1alpha1.KeyPhaseFailed, done: true},
			{phase: v1alpha1.KeyPhaseCreating, done: true},
			{phase: v1alpha1.KeyPhaseActive, done: true},
		}
		for _, tc := range testCases {
			t.Run(string(tc.phase), func(t *testing.T) {
				key.Status.Phase = tc.phase
				key.Finalizers = []string{v1alpha1.Finalizer}
				result := gu.Create(context.Background())
				assert.Equal(t, tc.done, result.IsDoneState())
				if tc.phase == "" || tc.phase == v1alpha1.KeyPhaseFailed {
					assert.Equal(t, v1alpha1.KeyPhaseCreating, key.Status.Phase)
				}
			})
		}
	})
}

func TestGuardian_Delete(t *testing.T) {
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	key := &v1alpha1.Key{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test",
			Namespace: "default",
		},
		Spec: v1alpha1.KeySpec{
			Provider: "eks",
			AccountRef: v1alpha1.AccountReference{
				ProviderConfigName: "test-config",
				AccountID:          "123456789",
			},
		},
	}
	cli := fake.NewClientBuilder().WithScheme(s).WithObjects(key).WithStatusSubresource(key).Build()

	c := &task.Context{
		Local:  k8s.NewMutClientWithOwner(cli, key.DeepCopy(), namer.NewNamer()).Client(),
		Logger: logr.Discard(),
		Key:    key,
		Config: config.RootConfig{},
	}
	gu := NewGuardian(c)

	t.Run("Delete succeeds when no dependencies", func(t *testing.T) {
		result := gu.Delete(context.Background())
		assert.True(t, result.IsDoneState())
	})
}
