package eks

import (
	"context"
	"testing"
	"time"

	"github.com/crossplane-contrib/provider-aws/apis/ec2/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/stretchr/testify/assert"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/pkg/util"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"
)

func fakeSubnetTask(t *testing.T) (*Subnet, *VPC) {
	c := fakeTaskContext(t)
	c.Config = config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			RegionConfig: config.RegionConfig{
				Name:  "us-west-2",
				Zones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
			},
		},
	}
	vpcTask := NewVPC(c)
	subnetTask := NewSubnet(c)
	return subnetTask, vpcTask
}

func TestSubnet_Create(t *testing.T) {
	t.Run("get vpcRes error", func(t *testing.T) {
		subnetTask, _ := fakeSubnetTask(t)
		result := subnetTask.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)
	})
	t.Run("create subnet success", func(t *testing.T) {
		subnetTask, vpcTask := fakeSubnetTask(t)
		result := vpcTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		result = subnetTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		name := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
		zones := vpcTask.GetZones()
		workers, public, private, err := GetAWSCIDR(subnetTask.VPC.Spec.CIDR, subnetTask.VPC.Spec.Region, len(zones))
		assert.Nil(t, err)
		for idx, nodeSubnet := range subnetTask.nodeSubnets {
			assert.Equal(t, nodeSubnet.Name, namer.NodesSubnetName(name, zones[idx]))
			assert.Equal(t, nodeSubnet.Spec.ForProvider.CIDRBlock, workers[idx])
			assert.Equal(t, nodeSubnet.Spec.ForProvider.AvailabilityZone, pointer.String(zones[idx]))
			assert.Equal(t, nodeSubnet.Spec.ForProvider.VPCIDRef.Name, name)
		}

		for idx, publicSubnet := range subnetTask.publicSubnets {
			assert.Equal(t, publicSubnet.Name, namer.PublicSubnetName(name, zones[idx]))
			assert.Equal(t, publicSubnet.Spec.ForProvider.CIDRBlock, public[idx])
			assert.Equal(t, publicSubnet.Spec.ForProvider.AvailabilityZone, pointer.String(zones[idx]))
			assert.Equal(t, publicSubnet.Spec.ForProvider.VPCIDRef.Name, name)
		}

		for idx, privateSubnet := range subnetTask.privateSubnets {
			assert.Equal(t, privateSubnet.Name, namer.PrivateSubnetName(name, zones[idx]))
			assert.Equal(t, privateSubnet.Spec.ForProvider.CIDRBlock, private[idx])
			assert.Equal(t, privateSubnet.Spec.ForProvider.AvailabilityZone, pointer.String(zones[idx]))
			assert.Equal(t, privateSubnet.Spec.ForProvider.VPCIDRef.Name, name)
		}
	})
}

func TestSubnet_createNodeSubnet(t *testing.T) {
	subnetTask, vpcTask := fakeSubnetTask(t)
	result := vpcTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	name := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
	key := types.NamespacedName{
		Name: name,
	}
	vpcRes := &v1beta1.VPC{}
	err := subnetTask.Local.Get(context.Background(), key, vpcRes)
	assert.Nil(t, err)
	tags := cloudv1.TagsFromLabels(vpcTask.VPC.Labels)
	cidr := "10.16.0.0/21"
	zone := "us-west-2a"
	t.Run("create node subnet success", func(t *testing.T) {
		subnet, err := subnetTask.createNodeSubnet(context.Background(), zone, cidr, vpcRes, tags)
		assert.Nil(t, err)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{Name: subnetTask.ProviderConfigName()},
			},
			ForProvider: v1beta1.SubnetParameters{
				Region:           vpcRes.Spec.ForProvider.Region,
				CIDRBlock:        cidr,
				AvailabilityZone: pointer.String(zone),
				Tags:             util.GetEC2V1BetaTags(util.AddResourceName(tags, namer.NodesSubnetName(name, "us-west-2a"))),
				VPCIDRef: &xpv1.Reference{
					Name: name,
				},
			},
		}, subnet.Spec)
	})
}

func TestSubnet_createPublicSubnet(t *testing.T) {
	subnetTask, vpcTask := fakeSubnetTask(t)
	result := vpcTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	name := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
	key := types.NamespacedName{
		Name: name,
	}
	vpcRes := &v1beta1.VPC{}
	err := subnetTask.Local.Get(context.Background(), key, vpcRes)
	assert.Nil(t, err)
	tags := cloudv1.TagsFromLabels(vpcTask.VPC.Labels)
	cidr := "10.16.0.0/21"
	zone := "us-west-2a"
	t.Run("create public subnet success", func(t *testing.T) {
		subnet, err := subnetTask.createPublicSubnet(context.Background(), zone, cidr, vpcRes, tags)
		assert.Nil(t, err)
		tags := util.AddResourceName(tags, namer.PublicSubnetName(name, zone))
		tagsetting := util.GetEC2V1BetaTags(tags)

		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{Name: subnetTask.ProviderConfigName()},
			},
			ForProvider: v1beta1.SubnetParameters{
				Region:           vpcRes.Spec.ForProvider.Region,
				CIDRBlock:        cidr,
				Tags:             tagsetting,
				AvailabilityZone: pointer.String(zone),
				VPCIDRef: &xpv1.Reference{
					Name: name,
				},
			},
		}, subnet.Spec)
	})
}

func TestSubnet_createPrivateSubnet(t *testing.T) {
	subnetTask, vpcTask := fakeSubnetTask(t)
	result := vpcTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	name := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
	key := types.NamespacedName{
		Name: name,
	}
	vpcRes := &v1beta1.VPC{}
	err := subnetTask.Local.Get(context.Background(), key, vpcRes)
	assert.Nil(t, err)
	tags := cloudv1.TagsFromLabels(vpcTask.VPC.Labels)
	cidr := "10.16.0.0/21"
	zone := "us-west-2a"
	t.Run("create private subnet success", func(t *testing.T) {
		subnet, err := subnetTask.createPrivateSubnet(context.Background(), zone, cidr, vpcRes, tags)
		assert.Nil(t, err)
		tags := util.AddResourceName(tags, namer.PrivateSubnetName(name, zone))
		tagsetting := util.GetEC2V1BetaTags(tags)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{Name: subnetTask.ProviderConfigName()},
			},
			ForProvider: v1beta1.SubnetParameters{
				Region:           vpcRes.Spec.ForProvider.Region,
				CIDRBlock:        cidr,
				Tags:             tagsetting,
				AvailabilityZone: pointer.String(zone),
				VPCIDRef: &xpv1.Reference{
					Name: name,
				},
			},
		}, subnet.Spec)
	})
}

func TestSubnet_Delete(t *testing.T) {
	subnetTask, vpcTask := fakeSubnetTask(t)
	result := vpcTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	result = subnetTask.Create(context.Background())
	assert.True(t, result.IsDoneState())
	zones := vpcTask.GetZones()
	t.Run("delete subnet success", func(t *testing.T) {
		result := subnetTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
		for _, zone := range zones {
			name := namer.UniqueIdentifier(vpcTask.VPC.ObjectMeta)
			key := types.NamespacedName{
				Name: namer.NodesSubnetName(name, zone),
			}
			subnet := &v1beta1.Subnet{}
			err := subnetTask.Local.Get(context.Background(), key, subnet)
			assert.ErrorContains(t, err, "not found")

			key = types.NamespacedName{
				Name: namer.PublicSubnetName(name, zone),
			}
			subnet = &v1beta1.Subnet{}
			err = subnetTask.Local.Get(context.Background(), key, subnet)
			assert.ErrorContains(t, err, "not found")

			key = types.NamespacedName{
				Name: namer.PrivateSubnetName(name, zone),
			}
			err = subnetTask.Local.Get(context.Background(), key, subnet)
			assert.ErrorContains(t, err, "not found")
		}
	})
}
