package eks

import (
	"context"
	"testing"
	"time"

	"github.com/crossplane-contrib/provider-aws/apis/ec2/v1beta1"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"
)

func fakeSecurityGroupTask(t *testing.T) (*SecurityGroup, *VPC) {
	c := fakeTaskContext(t)
	vpcTask := NewVPC(c)
	sgTask := NewSecurityGroup(c)
	return sgTask, vpcTask
}

func TestSecurityGroup_Create(t *testing.T) {
	t.Run("create security group success", func(t *testing.T) {
		sgTask, vpcTask := fakeSecurityGroupTask(t)
		result := vpcTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		sgRes := &v1beta1.SecurityGroup{}
		vpcResName := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
		sgResName := vpcResName
		err := sgTask.Local.Get(context.Background(), types.NamespacedName{Name: sgResName}, sgRes)
		assert.Nil(t, err)
		assert.Equal(t, sgRes.Spec.ForProvider.VPCIDRef.Name, vpcResName)
		assert.Equal(t, sgRes.Spec.ForProvider.Ingress, []v1beta1.IPPermission{
			{
				IPProtocol: "tcp",
				FromPort:   pointer.Int32(443),
				ToPort:     pointer.Int32(443),
				IPRanges: []v1beta1.IPRange{
					{CIDRIP: "0.0.0.0/0"},
				},
			},
			{
				IPProtocol: "tcp",
				FromPort:   pointer.Int32(80),
				ToPort:     pointer.Int32(80),
				IPRanges: []v1beta1.IPRange{
					{CIDRIP: "0.0.0.0/0"},
				},
			},
			{
				IPProtocol: "tcp",
				FromPort:   pointer.Int32(9090),
				ToPort:     pointer.Int32(9090),
				IPRanges: []v1beta1.IPRange{
					{CIDRIP: "0.0.0.0/0"},
				},
			},
			{
				IPProtocol: "-1",
				IPRanges: []v1beta1.IPRange{
					{CIDRIP: "10.0.0.0/8"},
					{CIDRIP: "172.16.0.0/12"},
					{CIDRIP: "192.168.0.0/16"},
				},
			},
		})
	})
	t.Run("get vpcRes error", func(t *testing.T) {
		sgTask, _ := fakeSecurityGroupTask(t)
		result := sgTask.Create(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 TestSecurityGroup_PostCreate(t *testing.T) {
	t.Run("create security group success", func(t *testing.T) {
		sgTask, vpcTask := fakeSecurityGroupTask(t)
		result := vpcTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		sgRes := &v1beta1.SecurityGroup{}
		vpcResName := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
		sgResName := vpcResName
		err := sgTask.Local.Get(context.Background(), types.NamespacedName{Name: sgResName}, sgRes)
		sgRes.Status.AtProvider.SecurityGroupID = "sg-123456"
		assert.Nil(t, sgTask.Local.Status().Update(context.Background(), sgRes))
		assert.Nil(t, err)
		result = sgTask.PostCreate(context.Background())
		assert.True(t, result.IsDoneState())
	})
	t.Run("aws sg not found", func(t *testing.T) {
		sgTask, _ := fakeSecurityGroupTask(t)
		result := sgTask.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)
	})
	t.Run("aws sg is not available", func(t *testing.T) {
		sgTask, vpcTask := fakeSecurityGroupTask(t)
		result := vpcTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		sgRes := &v1beta1.SecurityGroup{}
		vpcResName := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
		sgResName := vpcResName
		err := sgTask.Local.Get(context.Background(), types.NamespacedName{Name: sgResName}, sgRes)
		result = sgTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "not available")
		assert.Equal(t, time.Second*30, res.RequeueAfter)
	})
}

func TestSecurityGroup_setSecurityGroupStatus(t *testing.T) {
	sgTask, _ := fakeSecurityGroupTask(t)
	sgResName := "sg-test"
	sgRes := &v1beta1.SecurityGroup{
		ObjectMeta: metav1.ObjectMeta{
			Name: sgResName,
		},
	}
	err := sgTask.Local.Create(context.Background(), sgRes)
	assert.Nil(t, err)
	err = sgTask.setSecurityGroupStatus(context.Background(), sgRes)
	assert.Nil(t, err)
	assert.Equal(t, sgRes.Status.AtProvider.SecurityGroupID, sgRes.Status.AtProvider.SecurityGroupID)
}

func TestSecurityGroup_Delete(t *testing.T) {
	t.Run("Delete aws sg success", func(t *testing.T) {
		sgTask, vpcTask := fakeSecurityGroupTask(t)
		result := vpcTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
		result = sgTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
	})
}

func TestSecurityGroup_PostDelete(t *testing.T) {
	sgTask, vpcTask := fakeSecurityGroupTask(t)
	result := vpcTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	result = sgTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	result = sgTask.Delete(context.Background())
	assert.True(t, result.IsDoneState())
	result = sgTask.PostDelete(context.Background())
	assert.True(t, result.IsDoneState())
}
