package e2e

import (
	"fmt"
	"time"

	"github.com/tidbcloud/infra-api/pkg/consts"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

var _ = Describe("aws unusual scene test", func() {
	interval := 10 * time.Second

	var (
		Region       string
		InstanceType string
	)

	BeforeEach(func() {
		Region = e2eEnv.Config.AWS.AnotherRegion
		InstanceType = e2eEnv.Config.AWS.InstanceType
	})

	It("Quary ProviderType which doesn't exist", func() {
		ProviderType := newProviderType("e2e")
		err := infraCli.Get(ctx, types.NamespacedName{Namespace: ProviderType.Namespace, Name: ProviderType.Name}, ProviderType)
		Expect(err).NotTo(BeNil())
		statusErr, ok := err.(*errors.StatusError)
		Expect(ok).Should(BeTrue())
		Expect(statusErr.ErrStatus.Code).Should(Equal(int32(404)))
	})

	It("Quary Provider which doesn't exist", func() {
		provider := newProvider("e2e")
		err := infraCli.Get(ctx, types.NamespacedName{Namespace: provider.Namespace, Name: provider.Name}, provider)
		Expect(err).NotTo(BeNil())
		statusErr, ok := err.(*errors.StatusError)
		Expect(ok).Should(BeTrue())
		Expect(statusErr.ErrStatus.Code).Should(Equal(int32(404)))
	})

	Describe("Create network which is incorrect", func() {
		It("Network created use incorrect provider", func() {
			Network := newCommonNetwork(Region, e2eEnv.Project, consts.ProviderTypeAWS)
			Network.Spec.Provider = "e2e"

			err := infraCli.Create(ctx, Network)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))

		})

		It("Network created use incorrect Region", func() {
			Network := newCommonNetwork("e2e", e2eEnv.Project, consts.ProviderTypeAWS)
			err := infraCli.Create(ctx, Network)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))
		})

		It("Network created use incorrect CIDR", func() {
			Network := newCommonNetwork(Region, e2eEnv.Project, consts.ProviderTypeAWS)
			Network.Spec.CIDR = "10.256.0.0/18"

			err := infraCli.Create(ctx, Network)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))
		})

		It("Create  two networks in the same region", func() {
			By("create second network")
			Network2 := newCommonNetwork(e2eEnv.Config.AWS.Region, e2eEnv.Project, consts.ProviderTypeAWS)
			err := infraCli.Create(ctx, Network2)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))
		})
	})

	Describe("Create network peering which is unusual", func() {
		It("Network doesn't exist, creat network peering should fail", func() {
			NetworkPeer := &cloudv1.NetworkPeer{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: e2eEnv.Namespace,
					Name:      fmt.Sprintf("e2e-peer-%s", cloudv1.RandomString(6)),
					Labels:    e2eResourceLabels(),
				},
				Spec: cloudv1.NetworkPeerSpec{
					Source: "e2e",
					Target: cloudv1.Target{
						AWS: &cloudv1.AWS{
							AccountID:  e2eEnv.Config.AWS.AccountID,
							VpcID:      e2eEnv.Config.AWS.VPCID,
							PeerCIDR:   e2eEnv.Config.AWS.VPCCIDR,
							PeerRegion: e2eEnv.Config.AWS.Region,
						},
					},
				},
			}
			err := infraCli.Create(ctx, NetworkPeer)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))
		})

		It("Network peering target region is not same with network region, create network peering should success", func() {
			By("create network peering which region is not same with network region")
			NetworkPeer := &cloudv1.NetworkPeer{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: e2eEnv.Namespace,
					Name:      fmt.Sprintf("e2e-peer-%s", cloudv1.RandomString(6)),
					Labels:    e2eResourceLabels(),
				},
				Spec: cloudv1.NetworkPeerSpec{
					Source: e2eEnv.AwsNetwork.Name,
					Target: cloudv1.Target{
						AWS: &cloudv1.AWS{
							AccountID:  e2eEnv.Config.AWS.AccountID,
							VpcID:      e2eEnv.Config.AWS.AnotherRegionVPCID,
							PeerCIDR:   e2eEnv.Config.AWS.AnotherRegionVPCCIDR,
							PeerRegion: e2eEnv.Config.AWS.AnotherRegion,
						},
					},
				},
			}
			Expect(infraCli.Create(ctx, NetworkPeer)).Should(Succeed())
			Expect(infraCli.Get(ctx, types.NamespacedName{Namespace: NetworkPeer.Namespace, Name: NetworkPeer.Name}, NetworkPeer)).Should(Succeed())
			waitAWSNetworkPeerReady(ctx, NetworkPeer, 5*time.Minute, interval)

			By("create secondary network peering")
			NetworkPeerSec := &cloudv1.NetworkPeer{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: e2eEnv.Namespace,
					Name:      fmt.Sprintf("e2e-peer-%s", cloudv1.RandomString(6)),
					Labels:    e2eResourceLabels(),
				},
				Spec: cloudv1.NetworkPeerSpec{
					Source: e2eEnv.AwsNetwork.Name,
					Target: cloudv1.Target{
						AWS: &cloudv1.AWS{
							AccountID:  e2eEnv.Config.AWS.AccountID,
							VpcID:      e2eEnv.Config.AWS.VPCID,
							PeerCIDR:   e2eEnv.Config.AWS.VPCCIDR,
							PeerRegion: e2eEnv.Config.AWS.Region,
						},
					},
				},
			}
			Expect(infraCli.Create(ctx, NetworkPeerSec)).Should(Succeed())
			Expect(infraCli.Get(ctx, types.NamespacedName{Namespace: NetworkPeerSec.Namespace, Name: NetworkPeerSec.Name}, NetworkPeerSec)).Should(Succeed())
			waitAWSNetworkPeerReady(ctx, NetworkPeerSec, 5*time.Minute, interval)

			By("delete correct network peering")
			Expect(infraCli.Delete(ctx, NetworkPeer)).Should(Succeed())
			Expect(infraCli.Delete(ctx, NetworkPeerSec)).Should(Succeed())

		})

		It("the CIDR is overlapping, creat network peering should fail", func() {
			By("create correct network")
			project := fmt.Sprintf("%d", time.Now().UnixNano())
			AnotherRegionNetwork := newCommonNetwork(Region, project, consts.ProviderTypeAWS)
			AnotherRegionNetwork.Spec.CIDR = "10.0.250.0/24"
			Expect(infraCli.Create(ctx, AnotherRegionNetwork)).Should(Succeed())
			waitNetworkReady(ctx, AnotherRegionNetwork, 45*time.Minute, interval)

			By("create network peering which CIDR is overlapping")
			NetworkPeer := &cloudv1.NetworkPeer{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: e2eEnv.Namespace,
					Name:      fmt.Sprintf("e2e-peer-%s", cloudv1.RandomString(6)),
					Labels:    e2eResourceLabels(),
				},
				Spec: cloudv1.NetworkPeerSpec{
					Source: AnotherRegionNetwork.Name,
					Target: cloudv1.Target{
						AWS: &cloudv1.AWS{
							AccountID:  e2eEnv.Config.AWS.AccountID,
							VpcID:      e2eEnv.Config.AWS.VPCID,
							PeerCIDR:   e2eEnv.Config.AWS.VPCCIDR,
							PeerRegion: e2eEnv.Config.AWS.Region,
						},
					},
				},
			}

			err := infraCli.Create(ctx, NetworkPeer)
			Expect(err).ShouldNot(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Code).Should(Equal(int32(422)))
			Expect(statusErr.ErrStatus.Message).Should(ContainSubstring("overlapped"))

			By("delete correct network")
			Expect(infraCli.Delete(ctx, AnotherRegionNetwork)).Should(Succeed())
			waitNetworkDeleted(ctx, AnotherRegionNetwork, 15*time.Minute, interval)
		})
	})

	Describe("Create cluster which is incorrect", func() {
		It("Network doesn't exist, create cluster should fail", func() {
			Cluster := newCommonCluster("e2e", InstanceType)
			err := infraCli.Create(ctx, Cluster)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))

		})

		It("create cluster ,but the instanceType doesn't support", func() {
			By("create cluster ,but the instanceType doesn't support")
			instanceType := "e2e"
			Cluster := newCommonCluster(e2eEnv.AwsNetwork.Name, InstanceType)
			Cluster.Spec.Cluster.TiDB.InstanceType = instanceType
			Cluster.Spec.Cluster.TiKV.InstanceType = instanceType
			Cluster.Spec.Cluster.PD.InstanceType = instanceType
			Cluster.Spec.Cluster.TiFlash.InstanceType = instanceType

			err := infraCli.Create(ctx, Cluster)
			Expect(err).NotTo(BeNil())
			statusErr, ok := err.(*errors.StatusError)
			Expect(ok).Should(BeTrue())
			Expect(statusErr.ErrStatus.Status).Should(Equal("Failure"))
			Expect(statusErr.ErrStatus.Reason).Should(Equal(metav1.StatusReasonInvalid))
		})
	})
})
