package cluster

import (
	"context"
	"fmt"
	"testing"

	"github.com/go-logr/logr"
	. "github.com/onsi/gomega"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	gardencorev1beta1 "github.com/tidbcloud/infra-api/pkg/gardener/core/apis/v1beta1"
	"github.com/tidbcloud/infra-api/pkg/gitops"
	"github.com/tidbcloud/infra-api/pkg/util"
)

func TestPublicEndpointAddress(t *testing.T) {
	cases := map[string]struct {
		cluster  *cloudv1.Cluster
		provider string
		expect   string
	}{
		"LegacyAWSPublicAddress": {
			cluster: &cloudv1.Cluster{
				ObjectMeta: metav1.ObjectMeta{
					Name: "123",
					Annotations: map[string]string{
						annoKeyUseLegacyDNS: annoValUseLegacyDNS,
					},
				},
			},
			provider: "aws",
			expect:   fmt.Sprintf("tidb.%s.%s.test-region.aws.pingcap.com", util.FnvHash("tidb123"), util.FnvHash("1")),
		},
		"LegacyGCPPublicAddress": {
			cluster: &cloudv1.Cluster{
				ObjectMeta: metav1.ObjectMeta{
					Name: "456",
					Annotations: map[string]string{
						annoKeyUseLegacyDNS: annoValUseLegacyDNS,
					},
				},
			},
			provider: "gcp",
			expect:   fmt.Sprintf("tidb.%s.%s.test-region.gcp.pingcap.com", util.FnvHash("tidb456"), util.FnvHash("1")),
		},
		"AWSPublicAddress": {
			cluster: &cloudv1.Cluster{
				ObjectMeta: metav1.ObjectMeta{
					Name: "123",
				},
				Spec: cloudv1.ClusterSpec{
					SqlEndpoints: cloudv1.SqlEndpoints{
						DNS: cloudv1.DNS{
							Identity: "test",
						},
					},
				},
			},
			provider: "aws",
			expect:   "tidb.test.tidb-cloud.com",
		},
		"GCPPublicAddress": {
			cluster: &cloudv1.Cluster{
				ObjectMeta: metav1.ObjectMeta{
					Name: "456",
				},
				Spec: cloudv1.ClusterSpec{
					SqlEndpoints: cloudv1.SqlEndpoints{
						DNS: cloudv1.DNS{
							Identity: "test",
						},
					},
				},
			},
			provider: "gcp",
			expect:   "tidb.test.tidb-cloud.com",
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			r := newFakeReconcileContext(tc.cluster, &ClusterReconciler{
				ProviderType:           tc.provider,
				publicHostedZoneDomain: "tidb-cloud.com",
			}, nil)
			g.Expect(r.publicEndpointAddress()).To(Equal(tc.expect))
		})
	}
}

func newFakeReconcileContext(
	cluster *cloudv1.Cluster,
	reconciler *ClusterReconciler,
	shootClient client.Client) *reconcileContext {
	var provider *cloudv1.Provider
	var network *cloudv1.Network

	storage := resource.MustParse("1000Gi")
	gardenSpec := &cloudv1.GardenerProviderSpec{
		EnableGracefulShutdownWebhook: nil,
		Reporter:                      cloudv1.ReporterConfig{},
		Proxy:                         cloudv1.ProxyConfig{},
		AWS: cloudv1.AwsConfig{
			PublicHostedZoneDomain: "aws.pingcap.com",
			AccountID:              "123456",
			ECRRegion:              "test-region",
			PcaArn:                 "test/arn",
			PcaRegion:              "test-region",
		},
		GCP: cloudv1.GcpConfig{
			PublicHostedZoneDomain: "gcp.pingcap.com",
			ProjectID:              "test-project",
			CertManagerRole:        "TODO",
		},
	}
	provider = &cloudv1.Provider{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: cloudv1.ProviderSpec{
			Type: reconciler.ProviderType,
			Regions: []cloudv1.Region{{
				Name:  "test-region",
				Zones: []string{"z-a", "z-b", "z-c"},
			}},
			InstanceTypes: []cloudv1.InstanceType{
				{
					Name:   "compute",
					CPU:    resource.MustParse("8"),
					Memory: resource.MustParse("16Gi"),
				},
				{
					Name:       "storage",
					CPU:        resource.MustParse("8"),
					Memory:     resource.MustParse("32Gi"),
					MinStorage: &storage,
					MaxStorage: &storage,
				},
				{
					Name:       "tiny",
					CPU:        resource.MustParse("1"),
					Memory:     resource.MustParse("2Gi"),
					MaxStorage: &storage,
				},
			},
			ForProvider: &runtime.RawExtension{
				Object: gardenSpec,
			},
		},
	}
	gardenNetworkStatus := &cloudv1.GardenerNetworkStatus{
		ShootName: "test-shoot",
	}
	network = &cloudv1.Network{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test",
			Namespace: "test",
			Labels: map[string]string{
				cloudv1.ProjectLabel: "1",
			},
		},
		Spec: cloudv1.NetworkSpec{
			Region:   "test-region",
			Provider: "test",
			CIDR:     "10.240.0.1/12",
		},
		Status: cloudv1.NetworkStatus{
			Phase: cloudv1.NetworkPhaseNormal,
			FromProvider: runtime.RawExtension{
				Object: gardenNetworkStatus,
			},
		},
	}
	cluster.Spec.NetworkName = network.Name

	configProvider := gitops.NewHardcode()

	shoot := new(gardencorev1beta1.Shoot)
	shoot.Spec.Kubernetes.Version = "1.18.7"

	r := &reconcileContext{
		ClusterReconciler:    reconciler,
		ctx:                  context.TODO(),
		providerGardenerSpec: gardenSpec,
		networkGardenStatus:  gardenNetworkStatus,
		shootClient:          shootClient,
		tc:                   &v1alpha1.TidbCluster{},
		Log:                  logr.Discard(),
		ClusterContext: ClusterContext{
			ConfigProvider: configProvider,
			KmsKeyId:       pointer.String("test-key"),
			KmsRegion:      pointer.String("test-region"),
			Target:         cluster,
			Network:        network,
			Provider:       provider,
		},
		shoot: shoot,
	}
	r.GitOps, r.Cluster = r, r
	return r
}
