package eks

import (
	"context"
	"testing"
	"time"

	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/stretchr/testify/assert"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/upbound/provider-aws/apis/ec2/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
)

func fakeInternetGatewayTask(t *testing.T) *InternetGatway {
	c := fakeTaskContext(t)
	igTask := NewInternetGateway(c)
	return igTask
}

func TestInternetGateway_Create(t *testing.T) {
	t.Run("create internet gateway success", func(t *testing.T) {
		igTask := fakeInternetGatewayTask(t)
		expectedTags := util.ToMapPointer(util.BuildAWSTagWithName(controllers.ConvertToCloudTags(igTask.Internet.Labels, cloudv1.ProviderTypeEKS), igTask.internetGatewayName()))
		if len(expectedTags) == 0 {
			expectedTags = nil
		}
		result := igTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		igRes := &v1beta1.InternetGateway{}
		igResName := igTask.internetGatewayName()
		err := igTask.Local.Get(context.Background(), types.NamespacedName{Name: igResName}, igRes)
		assert.Nil(t, err)
		assert.Equal(t, v1beta1.InternetGatewaySpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{Name: igTask.ProviderConfigName()},
				ManagementPolicies:      controllers.GetResourceManagementPolicies(),
			},
			ForProvider: v1beta1.InternetGatewayParameters_2{
				Region: pointer.String(igTask.Internet.Spec.Region),
				VPCIDRef: &xpv1.Reference{
					Name: igTask.VPC.Status.VPC.Name,
				},
				Tags: expectedTags,
			},
		}, igRes.Spec)
	})
	t.Run("update existing internet gateway tags", func(t *testing.T) {
		ctx := context.Background()
		igTask := fakeInternetGatewayTask(t)
		igTask.Internet.Labels = util.MergeMaps(igTask.Internet.Labels, map[string]string{
			"tags.tidbcloud.com/cp9-validation-revision": "v1",
		})
		result := igTask.Create(ctx)
		assert.True(t, result.IsDoneState())

		key := types.NamespacedName{Name: igTask.internetGatewayName()}
		igRes := &v1beta1.InternetGateway{}
		assert.NoError(t, igTask.Local.Get(ctx, key, igRes))
		igRes.Spec.ForProvider.Tags["external-controller-tag"] = pointer.String("keep")
		assert.NoError(t, igTask.Local.Update(ctx, igRes))

		igTask.Internet.Labels["tags.tidbcloud.com/cp9-validation-revision"] = "v2"
		igTask.Internet.Labels["tags.tidbcloud.com/cp9-validation-added"] = "yes"
		result = igTask.Create(ctx)
		assert.True(t, result.IsDoneState())

		got := &v1beta1.InternetGateway{}
		assert.NoError(t, igTask.Local.Get(ctx, key, got))
		assert.Equal(t, pointer.String("v2"), got.Spec.ForProvider.Tags["cp9-validation-revision"])
		assert.Equal(t, pointer.String("yes"), got.Spec.ForProvider.Tags["cp9-validation-added"])
		assert.Equal(t, pointer.String("keep"), got.Spec.ForProvider.Tags["external-controller-tag"])
		assert.Equal(t, "v2", got.Labels["tags.tidbcloud.com/cp9-validation-revision"])
		assert.Equal(t, "yes", got.Labels["tags.tidbcloud.com/cp9-validation-added"])
	})
}

func TestInternetGateway_PostCreate(t *testing.T) {
	t.Run("create internet gateway success", func(t *testing.T) {
		igTask := fakeInternetGatewayTask(t)
		result := igTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		igRes := &v1beta1.InternetGateway{}
		igResName := igTask.internetGatewayName()
		err := igTask.Local.Get(context.Background(), types.NamespacedName{Name: igResName}, igRes)
		igRes.Status.AtProvider.ID = pointer.String("ig-123456")
		igRes.Status.SetConditions(xpv1.Available())
		assert.Nil(t, igTask.Local.Status().Update(context.Background(), igRes))
		assert.Nil(t, err)
		result = igTask.PostCreate(context.Background())
		assert.True(t, result.IsDoneState())
	})
	t.Run("aws ig not found", func(t *testing.T) {
		igTask := fakeInternetGatewayTask(t)
		result := igTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "not found")
		assert.Equal(t, time.Second*30, res.RequeueAfter)
	})
}

func TestInternetGateway_setInternetGatewayStatus(t *testing.T) {
	igTask := fakeInternetGatewayTask(t)
	igResName := "ig-test"
	igId := "ig-123456"
	igRes := &v1beta1.InternetGateway{
		ObjectMeta: metav1.ObjectMeta{
			Name: igResName,
		},
		Status: v1beta1.InternetGatewayStatus{
			AtProvider: v1beta1.InternetGatewayObservation_2{
				ID: pointer.String(igId),
			},
		},
	}
	t.Run("Set ig info to vpc status", func(t *testing.T) {
		err := igTask.setInternetGatewayStatus(context.Background(), igRes)
		assert.Nil(t, err)
		assert.Equal(t, v1alpha1.InternetGateway{
			Name:              igResName,
			InternetGatewayID: igId,
		}, igTask.Internet.Status.InternetGateway)
	})
}

func TestInternetGateway_Delete(t *testing.T) {
	igTask := fakeInternetGatewayTask(t)
	t.Run("Delete aws ig success", func(t *testing.T) {
		result := igTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = igTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
	})
	t.Run("Delete aws ig not found", func(t *testing.T) {
		result := igTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
	})
}

func TestInternetGateway_PostDelete(t *testing.T) {
	igTask := fakeInternetGatewayTask(t)
	t.Run("Delete aws ig success", func(t *testing.T) {
		result := igTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = igTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
		result = igTask.PostDelete(context.Background())
		assert.True(t, result.IsDoneState())
	})
	t.Run("Delete aws ig not found", func(t *testing.T) {
		result := igTask.PostDelete(context.Background())
		assert.True(t, result.IsDoneState())
	})
}
