package cluster

import (
	"context"
	"fmt"

	"github.com/pingcap/errors"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/infra-api/pkg/config"
	"github.com/tidbcloud/infra-api/pkg/k8s"
	"github.com/tidbcloud/infra-api/pkg/o11y/metric_agent"
	tracerutil "github.com/tidbcloud/infra-api/pkg/tracer"
)

const (
	promRetention = "1d"
)

// ensureTidbMonitor creates or updates prometheus-external-configmap and tidb-monitor.
func (r *reconcileContext) ensureTidbMonitor(ctx context.Context) (errResult error) {
	_, span := tracer.Start(ctx, "ensureTidbMonitor")
	defer func() { tracerutil.EndSpan(span, errResult) }()

	if r.tc == nil {
		return errors.New("waiting for tidb cluster get ensured")
	}

	// Mount external configmap for prometheus to drop some tikv prometheus metrics.
	// Jira: https://jira.tidbcloud.com/browse/DM-3082
	// Doc: https://docs.pingcap.com/zh/tidb-in-kubernetes/stable/monitor-a-tidb-cluster
	tenantID, projectID, _ := metric_agent.GetIDsFromClusterCRD(r.Target, r.Network.Name)
	cm := &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: r.tc.Namespace,
			Name:      externalConfigName,
		}}
	// Completely override prometheus-external-config.
	if _, err := controllerutil.CreateOrUpdate(r.ctx, r.shootClient, cm, func() error {
		newCm, err := ExternalPrometheusConfig(r.Client, r.tc.Namespace, &ExternalLabels{
			ClusterName:  fmt.Sprintf("shoot/%s/%s/%s", r.Network.Spec.Provider, r.Network.Spec.Region, r.shoot.Status.TechnicalID),
			Project:      projectID,
			ShootName:    r.networkGardenStatus.ShootName,
			SeedRegion:   r.Network.Spec.Region,
			SeedProvider: r.Network.Spec.Provider,
			ShootInfra:   r.Network.Spec.Provider,
			TenantID:     tenantID,
			ClusterID:    r.tc.Namespace,
		})
		if err != nil {
			return err
		}
		newCm.DeepCopyInto(cm)
		return nil
	}); err != nil {
		return err
	}

	tm := NewDefaultTidbMonitor(r.tc.Namespace)
	tm.ObjectMeta.Name = r.tc.Name
	tm.ObjectMeta.Namespace = r.tc.Namespace
	tm.Spec.Initializer.Version = r.tc.Spec.Version
	tm.Spec.Clusters = []v1alpha1.TidbClusterRef{{Name: r.tc.GetName(), Namespace: r.tc.GetNamespace()}}
	if err := controllerutil.SetOwnerReference(r.tc, tm, r.Scheme); err != nil {
		return err
	}
	// Only override Prometheus field.
	if _, err := controllerutil.CreateOrUpdate(r.ctx, r.shootClient, tm, func() error {
		newTm := NewDefaultTidbMonitor(r.tc.Namespace)
		tm.Spec.Prometheus = newTm.Spec.Prometheus
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func NewDefaultTidbMonitor(namespace string) *v1alpha1.TidbMonitor {
	pullIfNotPresent := corev1.PullIfNotPresent
	// TODO: configuration should be managed in GitOPS
	return &v1alpha1.TidbMonitor{
		Spec: v1alpha1.TidbMonitorSpec{
			Clusters: []v1alpha1.TidbClusterRef{},
			Prometheus: v1alpha1.PrometheusSpec{
				MonitorContainer: v1alpha1.MonitorContainer{
					BaseImage:       config.GetBaseImage(config.PrometheusImage),
					Version:         config.PrometheusVersion,
					ImagePullPolicy: &pullIfNotPresent,
				},
				LogLevel: "info",
				Service: v1alpha1.ServiceSpec{
					Type: corev1.ServiceTypeNodePort,
				},
				Config: &v1alpha1.PrometheusConfiguration{
					ConfigMapRef: &v1alpha1.ConfigMapRef{
						Name:      externalConfigName,
						Namespace: pointer.String(namespace),
					},
				},
				RetentionTime: pointer.String(promRetention),
			},
			Grafana: &v1alpha1.GrafanaSpec{
				MonitorContainer: v1alpha1.MonitorContainer{
					BaseImage:       config.GetBaseImage(config.GrafanaImage),
					Version:         config.GrafanaVersion,
					ImagePullPolicy: &pullIfNotPresent,
				},
				LogLevel: "info",
				Service: v1alpha1.ServiceSpec{
					Type: corev1.ServiceTypeNodePort,
				},
				Username: "admin",
				Password: "admin",
				Envs: map[string]string{
					"GF_AUTH_ANONYMOUS_ENABLED":  "true",
					"GF_AUTH_ANONYMOUS_ORG_NAME": "Main Org.",
					"GF_AUTH_ANONYMOUS_ORG_ROLE": "Viewer",
				},
			},
			Reloader: v1alpha1.ReloaderSpec{
				MonitorContainer: v1alpha1.MonitorContainer{
					BaseImage:       config.GetBaseImage(config.TiDBMonitorReloaderImage),
					Version:         config.TiDBMonitorReloaderVersion,
					ImagePullPolicy: &pullIfNotPresent,
				},
				Service: v1alpha1.ServiceSpec{
					Type: corev1.ServiceTypeNodePort,
				},
			},
			Initializer: v1alpha1.InitializerSpec{
				MonitorContainer: v1alpha1.MonitorContainer{
					BaseImage:       config.GetBaseImage(config.TiDBMonitorInitializerImage),
					ImagePullPolicy: &pullIfNotPresent,
				},
				Envs: map[string]string{},
			},
			ImagePullPolicy:   pullIfNotPresent,
			Persistent:        true,
			StorageClassName:  pointer.String(k8s.DefaultStorageClassName),
			Storage:           "50Gi",
			KubePrometheusURL: pointer.String("http://prometheus-server.monitor.svc"),
			NodeSelector:      map[string]string{},
			Annotations:       map[string]string{},
			Tolerations:       []corev1.Toleration{},
		},
	}
}
