package gardener

import (
	"strings"
	"testing"
	"time"

	. "github.com/onsi/gomega"

	"github.com/prometheus/client_golang/prometheus/testutil"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	gardenerapi "github.com/tidbcloud/infra-api/pkg/gardener/core/apis/v1beta1"
)

func TestMetricsCollector_Collect(t *testing.T) {
	tests := []struct {
		name           string
		objs           []client.Object
		expecedMetrics string
	}{
		{
			name: "basic metrics collector function - shoot",
			objs: []client.Object{
				&gardenerapi.Shoot{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test",
						Namespace: "test",
						Labels: map[string]string{
							"tenant":       "1",
							"project":      "1",
							shootStatusKey: "healthy",
						},
					},
					Spec: gardenerapi.ShootSpec{
						Addons: &gardenerapi.Addons{
							KubernetesDashboard: &gardenerapi.KubernetesDashboard{
								Addon: gardenerapi.Addon{
									Enabled: false,
								},
								AuthenticationMode: new(string),
							},
							NginxIngress: &gardenerapi.NginxIngress{
								Addon:                    gardenerapi.Addon{Enabled: false},
								LoadBalancerSourceRanges: []string{},
								Config:                   map[string]string{},
							},
						},
						CloudProfileName: "",
						DNS: &gardenerapi.DNS{
							Domain: new(string),
							Providers: []gardenerapi.DNSProvider{
								{
									Domains: &gardenerapi.DNSIncludeExclude{
										Include: []string{},
										Exclude: []string{},
									},
									Primary:    new(bool),
									SecretName: new(string),
									Type:       new(string),
									Zones: &gardenerapi.DNSIncludeExclude{
										Include: []string{},
										Exclude: []string{},
									},
								},
							},
						},
						Extensions: []gardenerapi.Extension{
							{
								Type:           "",
								ProviderConfig: &gardenerapi.ProviderConfig{},
							},
						},
						Hibernation: &gardenerapi.Hibernation{
							Enabled: new(bool),
							Schedules: []gardenerapi.HibernationSchedule{
								{
									Start:    new(string),
									End:      new(string),
									Location: new(string),
								},
							},
						},
						Kubernetes: gardenerapi.Kubernetes{
							AllowPrivilegedContainers: new(bool),
							ClusterAutoscaler: &gardenerapi.ClusterAutoscaler{
								ScaleDownDelayAfterAdd: &metav1.Duration{
									Duration: 0,
								},
								ScaleDownDelayAfterDelete: &metav1.Duration{
									Duration: 0,
								},
								ScaleDownDelayAfterFailure: &metav1.Duration{
									Duration: 0,
								},
								ScaleDownUnneededTime: &metav1.Duration{
									Duration: 0,
								},
								ScaleDownUtilizationThreshold: &resource.Quantity{
									Format: "",
								},
								ScanInterval: &metav1.Duration{
									Duration: 0,
								},
							},
							KubeAPIServer: &gardenerapi.KubeAPIServerConfig{
								KubernetesConfig: gardenerapi.KubernetesConfig{
									FeatureGates: map[string]bool{},
								},
								AdmissionPlugins: []gardenerapi.AdmissionPlugin{},
								APIAudiences:     []string{},
								AuditConfig: &gardenerapi.AuditConfig{
									AuditPolicy: &gardenerapi.AuditPolicy{
										ConfigMapRef: &corev1.ObjectReference{
											Kind:            "",
											Namespace:       "",
											Name:            "",
											UID:             "",
											APIVersion:      "",
											ResourceVersion: "",
											FieldPath:       "",
										},
									},
								},
								EnableBasicAuthentication: new(bool),
								OIDCConfig: &gardenerapi.OIDCConfig{
									CABundle: new(string),
									ClientAuthentication: &gardenerapi.OpenIDConnectClientAuthentication{
										ExtraConfig: map[string]string{},
										Secret:      new(string),
									},
									ClientID:       new(string),
									GroupsClaim:    new(string),
									GroupsPrefix:   new(string),
									IssuerURL:      new(string),
									RequiredClaims: map[string]string{},
									SigningAlgs:    []string{},
									UsernameClaim:  new(string),
									UsernamePrefix: new(string),
								},
								RuntimeConfig: map[string]bool{},
								ServiceAccountConfig: &gardenerapi.ServiceAccountConfig{
									Issuer: new(string),
									SigningKeySecret: &corev1.LocalObjectReference{
										Name: "",
									},
								},
							},
							KubeControllerManager: &gardenerapi.KubeControllerManagerConfig{
								KubernetesConfig: gardenerapi.KubernetesConfig{
									FeatureGates: map[string]bool{},
								},
								HorizontalPodAutoscalerConfig: &gardenerapi.HorizontalPodAutoscalerConfig{
									CPUInitializationPeriod: &metav1.Duration{
										Duration: 0,
									},
									DownscaleDelay: &metav1.Duration{
										Duration: 0,
									},
									DownscaleStabilization: &metav1.Duration{
										Duration: 0,
									},
									InitialReadinessDelay: &metav1.Duration{
										Duration: 0,
									},
									SyncPeriod: &metav1.Duration{
										Duration: 0,
									},
									Tolerance: &resource.Quantity{
										Format: "",
									},
									UpscaleDelay: &metav1.Duration{
										Duration: 0,
									},
								},
								NodeCIDRMaskSize: new(int32),
							},
							KubeScheduler: &gardenerapi.KubeSchedulerConfig{
								KubernetesConfig: gardenerapi.KubernetesConfig{
									FeatureGates: map[string]bool{},
								},
							},
							KubeProxy: &gardenerapi.KubeProxyConfig{
								KubernetesConfig: gardenerapi.KubernetesConfig{FeatureGates: map[string]bool{}},
							},
							Kubelet: &gardenerapi.KubeletConfig{
								KubernetesConfig: gardenerapi.KubernetesConfig{
									FeatureGates: map[string]bool{},
								},
								CPUCFSQuota:      new(bool),
								CPUManagerPolicy: new(string),
								EvictionHard: &gardenerapi.KubeletConfigEviction{
									MemoryAvailable:   new(string),
									ImageFSAvailable:  new(string),
									ImageFSInodesFree: new(string),
									NodeFSAvailable:   new(string),
									NodeFSInodesFree:  new(string),
								},
								EvictionMaxPodGracePeriod: new(int32),
								EvictionMinimumReclaim: &gardenerapi.KubeletConfigEvictionMinimumReclaim{
									MemoryAvailable: &resource.Quantity{
										Format: "",
									},
									ImageFSAvailable: &resource.Quantity{
										Format: "",
									},
									ImageFSInodesFree: &resource.Quantity{
										Format: "",
									},
									NodeFSAvailable: &resource.Quantity{
										Format: "",
									},
									NodeFSInodesFree: &resource.Quantity{
										Format: "",
									},
								},
								EvictionPressureTransitionPeriod: &metav1.Duration{
									Duration: 0,
								},
								EvictionSoft: &gardenerapi.KubeletConfigEviction{
									MemoryAvailable:   new(string),
									ImageFSAvailable:  new(string),
									ImageFSInodesFree: new(string),
									NodeFSAvailable:   new(string),
									NodeFSInodesFree:  new(string),
								},
								EvictionSoftGracePeriod: &gardenerapi.KubeletConfigEvictionSoftGracePeriod{
									MemoryAvailable: &metav1.Duration{
										Duration: 0,
									},
									ImageFSAvailable: &metav1.Duration{
										Duration: 0,
									},
									ImageFSInodesFree: &metav1.Duration{
										Duration: 0,
									},
									NodeFSAvailable: &metav1.Duration{
										Duration: 0,
									},
									NodeFSInodesFree: &metav1.Duration{
										Duration: 0,
									},
								},
								MaxPods:      new(int32),
								PodPIDsLimit: new(int64),
								ImagePullProgressDeadline: &metav1.Duration{
									Duration: 0,
								},
							},
							Version: "",
						},
						Networking: gardenerapi.Networking{
							Type:           "",
							ProviderConfig: &gardenerapi.ProviderConfig{},
							Pods:           new(string),
							Nodes:          new(string),
							Services:       new(string),
						},
						Maintenance: &gardenerapi.Maintenance{
							AutoUpdate: &gardenerapi.MaintenanceAutoUpdate{
								KubernetesVersion:   false,
								MachineImageVersion: false,
							},
							TimeWindow: &gardenerapi.MaintenanceTimeWindow{
								Begin: "",
								End:   "",
							},
							ConfineSpecUpdateRollout: new(bool),
						},
						Monitoring: &gardenerapi.Monitoring{
							Alerting: &gardenerapi.Alerting{
								EmailReceivers: []string{},
							},
						},
						Provider: gardenerapi.Provider{
							Type:                 "",
							ControlPlaneConfig:   &gardenerapi.ProviderConfig{},
							InfrastructureConfig: &gardenerapi.ProviderConfig{},
							Workers:              []gardenerapi.Worker{},
						},
						Region:                   "",
						SecretBindingName:        "",
						SeedName:                 new(string),
						LoadBalancerSourceRanges: []string{},
					},
					Status: gardenerapi.ShootStatus{
						Conditions:  []gardenerapi.Condition{},
						Constraints: []gardenerapi.Condition{},
						Gardener: gardenerapi.Gardener{
							ID:      "",
							Name:    "",
							Version: "",
						},
						IsHibernated: false,
						LastOperation: &gardenerapi.LastOperation{
							Description: "",
							LastUpdateTime: metav1.Time{
								Time: time.Time{},
							},
							Progress: 0,
							State:    "",
							Type:     "",
						},
						LastErrors:         []gardenerapi.LastError{},
						ObservedGeneration: 0,
						RetryCycleStartTime: &metav1.Time{
							Time: time.Time{},
						},
						SeedName:    new(string),
						TechnicalID: "",
						UID:         "",
						// TODO: runtime.RawExtension?
						// InfrastructureProviderStatus: &runtime.RawExtension{
						// 	Raw:    []byte{},
						// 	Object: nil,
						// },
					},
				},
			},
			expecedMetrics: `
			# HELP infra_gardener_resources_shoot_info Information and status of infra cluster.
            # TYPE infra_gardener_resources_shoot_info gauge
            infra_gardener_resources_shoot_info{hibernated="false",name="test",namespace="test",project="1",provider="",region="",status="healthy",tenant="1"} 1
			`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g := NewGomegaWithT(t)

			// initialize cli
			scheme := runtime.NewScheme()
			g.Expect(gardenerapi.AddToScheme(scheme)).To(Succeed())
			cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.objs...).Build()
			// initialize collector
			collector := &MetricsCollector{
				gardenCli: cli,
			}

			err := testutil.CollectAndCompare(collector, strings.NewReader(tt.expecedMetrics))
			// use err to benil when you add the case
			// g.Expect(err).To(BeNil())
			if err != nil {
				t.Fatal(err)
			}
		})
	}
}
