package vpc_endpoint

import (
	"context"
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
	"github.com/crossplane/provider-aws/apis/ec2/v1alpha1"
	ec2v1alpha1 "github.com/crossplane/provider-aws/apis/ec2/v1alpha1"
	"github.com/golang/mock/gomock"
	. "github.com/onsi/gomega"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes/scheme"
	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
	awsfake "github.com/tidbcloud/infra-api/pkg/providers/aws/fake"
	"github.com/tidbcloud/infra-api/pkg/providers/gcp"
)

func TestS3GatewayEndpoint(t *testing.T) {
	// Define crd object.
	provider := "aws"
	region := "us-west-2"
	vpcID01 := "vpc-fake-id-01"
	shootTechnicalID01 := "shoot--unit--test"
	shootName := "test"
	providerConfig := "provider-aws"
	version := 0
	provider01 := &cloudv1.Provider{
		ObjectMeta: metav1.ObjectMeta{
			Name: provider,
		},
		Spec: cloudv1.ProviderSpec{
			ForProvider: &runtime.RawExtension{
				Object: &cloudv1.GardenerProviderSpec{
					GCP: cloudv1.GcpConfig{
						ProjectID: "pingcap-gardener",
					},
				},
			},
		},
	}
	network01 := &cloudv1.Network{
		ObjectMeta: metav1.ObjectMeta{
			Name: "network_01",
		},
		Spec: cloudv1.NetworkSpec{
			Provider: provider,
			Region:   region,
		},
		Status: cloudv1.NetworkStatus{
			FromProvider: runtime.RawExtension{
				Object: &cloudv1.GardenerNetworkStatus{
					NodeRole: fmt.Sprintf("%s-%s", shootTechnicalID01, "node"),
					AWS: cloudv1.GardenerNetworkAWSStatus{
						VpcID: &vpcID01,
					},
				},
				Raw: nil,
			},
		},
	}

	// Set up context.
	g := NewGomegaWithT(t)
	g.Expect(ec2v1alpha1.SchemeBuilder.AddToScheme(scheme.Scheme)).Should(Succeed())
	g.Expect(cloudv1.AddToScheme(scheme.Scheme)).Should(Succeed())
	client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(network01).Build()
	ctx := context.Background()
	awsClient := awsfake.NewMockClient(t)
	awsClient.MockEc2Client.EXPECT().DescribeRouteTables(gomock.All(), gomock.All(), gomock.All()).Return(
		&ec2.DescribeRouteTablesOutput{
			RouteTables: []ec2types.RouteTable{
				{
					RouteTableId: aws.String("rtb_id"),
					Tags: []ec2types.Tag{
						{
							Key:   aws.String("k"),
							Value: aws.String("v"),
						},
					},
				},
			},
		}, nil).MinTimes(1)

	// Run the creation.
	op, name, err := DeployS3GatewayEndpoint(ctx, client, awsClient, vpcID01, shootTechnicalID01, map[string]string{}, network01, providerConfig, version)

	// Check the creation output.
	g.Expect(err).Should(Succeed())
	g.Expect(name).Should(Equal("shoot--unit--test-gateway-endpoint-s3"))
	// Assert vpc endpoint crd is created.
	g.Expect(op).Should(Equal(controllerutil.OperationResultCreated))
	// Assert network object has vpc endpoint name annotation.
	g.Expect(network01.Annotations).Should(HaveKeyWithValue(consts.S3GatewayEndpointNameAnnotationKey, name))

	// Run the deletion.
	gardenerStatus, err := cloudv1.GardenerProviderSpecForProvider(provider01)
	g.Expect(err).Should(Succeed())
	err = CleanupEndpoint(ctx, client, shootTechnicalID01, network01, gcp.NewNoOpGCPAPI(), gardenerStatus, shootName)

	// Check the deletion output.
	g.Expect(err).Should(Succeed())

	// Check the vpc endpoint has been deleted.
	endpoint := &v1alpha1.VPCEndpoint{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	err = client.Get(ctx, ctrlclient.ObjectKeyFromObject(endpoint), endpoint)
	g.Expect(errors.IsNotFound(err)).Should(Equal(true))
}
