package integration

import (
	"context"
	"time"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"github.com/upbound/provider-azure/apis/v1beta1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/ptr"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/namer"
)

const (
	k8sName = "integration-test-k8s"
)

// We should only check the resources directly managed by cluster-controller
// eg. providerconfig, nodepool, namespace, certificate, configmap, tidbcluster
var _ = Describe("Cluster controller", func() {
	var (
		tenantID     = "123"
		projectID    = "543"
		clusterID    = "123456789"
		namespace    = "ns-" + projectID
		instanceType = "standard"
	)

	Context("when cluster created", func() {
		It("Should create a cluster without error", func() {
			By("Create Cluster CR dependencies")
			// mock cluster-server behavior to create Cluster CR and its dependencies
			ns := &corev1.Namespace{
				ObjectMeta: metav1.ObjectMeta{
					Name: namespace,
				},
			}
			ctx := context.Background()
			err := k8sClient.Create(ctx, ns)
			Expect(err).NotTo(HaveOccurred())

			kubeconfigSecret, err := newKubeconfigSecret(namespace)
			Expect(err).NotTo(HaveOccurred())
			err = k8sClient.Create(ctx, kubeconfigSecret)
			Expect(err).NotTo(HaveOccurred())

			cr := newConfigRevision(namespace)
			err = k8sClient.Create(ctx, cr)
			Expect(err).NotTo(HaveOccurred())

			cm := newInitSQLConfigMap(namespace)
			err = k8sClient.Create(ctx, cm)
			Expect(err).NotTo(HaveOccurred())

			secret := newJwksSecret(namespace)
			err = k8sClient.Create(ctx, secret)
			Expect(err).NotTo(HaveOccurred())

			By("Create a Cluster")
			cluster := newCluster(namespace, tenantID, projectID, clusterID, instanceType)
			err = k8sClient.Create(ctx, cluster)
			Expect(err).NotTo(HaveOccurred())

			ep, err := cachedK8sProvider.GetEnvProvider(ctx, cluster)
			Expect(err).NotTo(HaveOccurred())
			remoteCli := ep.GetClient()
			time.Sleep(5 * time.Second)
			dbNsName := "tidb" + clusterID
			dbNs := &corev1.Namespace{}
			Expect(remoteCli.Get(ctx, types.NamespacedName{Name: dbNsName}, dbNs)).To(Succeed())

			pc := &v1beta1.ProviderConfig{}
			pcName := namer.NewName(k8sName, "pc", "", -1)
			Expect(k8sClient.Get(ctx, types.NamespacedName{Name: pcName}, pc)).To(Succeed())
		})
	})
})

func newConfigRevision(ns string) *cloudv1.ConfigRevision {
	return &cloudv1.ConfigRevision{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "config",
			Namespace: ns,
		},
	}
}

func newInitSQLConfigMap(ns string) *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "initsql",
			Namespace: ns,
		},
	}
}

func newKubeconfigSecret(ns string) (*corev1.Secret, error) {
	data, err := GenerateKubeConfiguration("", k8sConfig)
	if err != nil {
		return nil, err
	}
	return &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "integration-test-kubeconfig",
			Namespace: ns,
		},
		Data: map[string][]byte{
			"kubeconfig": data,
		},
	}, nil
}

func newJwksSecret(ns string) *corev1.Secret {
	return &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "jwks",
			Namespace: ns,
		},
	}
}

func newCluster(ns, tenantID, projectID, clusterID string, instanceType string) *cloudv1.Cluster {
	return &cloudv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      clusterID,
			Namespace: ns,
			Labels: map[string]string{
				cloudv1.TenantTagKey:       tenantID,
				cloudv1.ProjectTagKey:      projectID,
				cloudv1.ReconcilerLabelKey: clusterReconcilerLabel,
				cloudv1.RegionLabel:        azureRegion,
				cloudv1.ProviderTypeLabel:  consts.ProviderTypeAKS,
			},
		},
		Spec: cloudv1.ClusterSpec{
			Kubernetes: cloudv1.Kubernetes{
				Name:     k8sName,
				Provider: cloudv1.ProviderTypeAKS,
				Endpoint: "localhost",
				Account:  "account-id",
				Network:  "local-vpc",
				KubeConfigSecretRef: &corev1.SecretReference{
					Name:      "integration-test-kubeconfig",
					Namespace: ns,
				},
			},
			Cluster: cloudv1.ClusterTopology{
				TiDB: cloudv1.TiDB{
					Replicas:     2,
					InstanceType: instanceType,
				},
				TiKV: cloudv1.TiKV{
					Replicas:     3,
					InstanceType: instanceType,
					StorageSize:  resource.MustParse("1000Gi"),
				},
				PD: cloudv1.PD{
					Replicas:     3,
					InstanceType: instanceType,
					StorageSize:  resource.MustParse("10Gi"),
				},
				TiFlash: cloudv1.TiFlash{
					Replicas:     2,
					InstanceType: instanceType,
					StorageSize:  resource.MustParse("1000Gi"),
				},
			},
			SqlEndpoints: cloudv1.SqlEndpoints{
				Public: cloudv1.Service{
					Enabled: ptr.To(true),
				},
			},
			ConfigRevisionRef: &cloudv1.Reference{Name: "config"},
		},
	}
}
