package e2e

import (
	"fmt"
	"net"
	"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("gcp unusual scene test", func() {
	interval := 10 * time.Second

	var (
		InstanceType string
	)

	BeforeEach(func() {
		InstanceType = e2eEnv.Config.GCP.InstanceType
	})

	It("Create  two networks in the same region", func() {
		By("create second network")
		Network2 := newCommonNetwork(e2eEnv.Config.GCP.Region, e2eEnv.Project, consts.ProviderTypeGCP)
		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))
	})

	It("Create network peering which is unusual", 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.GcpNetwork.Name,
				Target: cloudv1.Target{
					GCP: &cloudv1.GCP{
						ProjectID:   e2eEnv.Config.GCP.ProjectID,
						PeerNetwork: e2eEnv.Config.GCP.AnotherRegionNetworkName, //in region us-east1
					},
				},
			},
		}
		Expect(infraCli.Create(ctx, NetworkPeer)).Should(Succeed())
		waitGCPNetworkPeerReady(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.GcpNetwork.Name,
				Target: cloudv1.Target{
					GCP: &cloudv1.GCP{
						ProjectID:   e2eEnv.Config.GCP.ProjectID,
						PeerNetwork: e2eEnv.Config.GCP.NetworkName,
					},
				},
			},
		}
		Expect(infraCli.Create(ctx, NetworkPeerSec)).Should(Succeed())
		waitGCPNetworkPeerReady(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("Connect cluster use a specific IP whitelist", func() {
		By("create correct cluster")
		Cluster := newCommonCluster(e2eEnv.GcpNetwork.Name, InstanceType)
		Expect(infraCli.Create(ctx, Cluster)).Should(Succeed())
		waitClusterReady(ctx, Cluster, 25*time.Minute, interval)

		By("update cluster use 183.209.115.85/32 IP whitelist")
		Expect(infraCli.Get(ctx, types.NamespacedName{Namespace: Cluster.Namespace, Name: Cluster.Name}, Cluster)).Should(Succeed())
		Cluster.Spec.SqlEndpoints.Public.LoadBalancerSourceRanges = []string{"183.209.115.85/32"}
		Expect(infraCli.Update(ctx, Cluster)).Should(Succeed())

		By("do cluster connect")
		Expect(Cluster.Status.SqlEndpoints.PublicEndpoint).NotTo(BeNil())
		Eventually(func() error {
			_, err := net.Dial("tcp", fmt.Sprintf("%s:4000", Cluster.Status.SqlEndpoints.PublicEndpoint.Host))
			if err == nil {
				return errWait
			}
			_, ok := err.(*net.OpError)
			Expect(ok).Should(BeTrue())
			return nil
		}, 10*time.Minute, interval).Should(Succeed(), "connect should fail")

		By("update cluster use 0.0.0.0/0 IP whitelist")
		Expect(infraCli.Get(ctx, types.NamespacedName{Namespace: Cluster.Namespace, Name: Cluster.Name}, Cluster)).Should(Succeed())
		Cluster.Spec.SqlEndpoints.Public.LoadBalancerSourceRanges = []string{"0.0.0.0/0"}
		Expect(infraCli.Update(ctx, Cluster)).Should(Succeed())

		By("do cluster connect")
		waitTiDBReady(ctx, Cluster, 25*time.Minute, interval)

		By("delete correct cluster")
		Expect(infraCli.Delete(ctx, Cluster)).Should(Succeed())
		waitClusterDeleted(ctx, Cluster, 15*time.Minute, interval)
	})
})
