package prometheus

import (
	"context"
	"fmt"
	"strings"
	"sync"

	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

// PromeClient manages prometheus
type PromeClient struct {
	sync.RWMutex
	k8sCli kubernetes.Interface
}

// NewPromeClient returns a Prometheus client that manages tidb cluster on k8s
func NewPromeClient(k8sCli kubernetes.Interface) *PromeClient {
	return &PromeClient{
		k8sCli: k8sCli,
	}
}

// CreateAPP creates app
func (c *PromeClient) CreateAPP(ctx context.Context, app *types.APPConfig) error {
	c.Lock()
	defer c.Unlock()

	err := c.checkOrCreateNamespace(app.Namespace)
	if err != nil {
		return errors.Trace(err)
	}

	if err = c.createPrometheusPod(ctx, app); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (c *PromeClient) checkOrCreateNamespace(namespace string) error {
	isExist, err := util.CheckNamespaceIsExist(namespace, c.k8sCli)
	if err != nil {
		return errors.Trace(err)
	}
	if !isExist {
		err = util.CreateK8sNamespace(namespace, c.k8sCli)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

func (c *PromeClient) createPrometheusPod(ctx context.Context, app *types.APPConfig) error {
	saveCfgAndRunCmd := fmt.Sprintf("sed -i 's/localhost:9090/%s/g' /etc/prometheus/prometheus.yml && /bin/prometheus --config.file=/etc/prometheus/prometheus.yml", app.Args)
	containers := []v1.Container{
		{
			Name:  util.FormatSnakeCase(app.Name),
			Image: getPrometheusImage(app.Version),
			Ports: []v1.ContainerPort{
				{
					ContainerPort: app.Port,
				},
			},
			Command: []string{
				"sh",
			},
			Args: []string{
				"-c",
				saveCfgAndRunCmd,
			},
			VolumeMounts: []v1.VolumeMount{
				{
					Name:      "timezone",
					ReadOnly:  true,
					MountPath: "/etc/localtime",
				},
			},
		},
	}

	meta := metav1.ObjectMeta{
		Name: util.FormatSnakeCase(app.Name),
	}

	pod, err := util.CreatePod(app.Namespace, c.k8sCli, &v1.Pod{
		ObjectMeta: meta,
		Spec: v1.PodSpec{
			Containers: containers,
			Volumes: []v1.Volume{
				{
					Name:         "timezone",
					VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/localtime"}},
				},
			},
		},
	})

	if err != nil && !strings.Contains(err.Error(), "already exists") {
		return errors.Trace(err)
	}

	if ok := util.CheckPodIsRunning(ctx, app.Namespace, app.Name, c.k8sCli); !ok {
		return errors.Errorf("Create pod %s failed", app.Name)
	}

	pod, err = util.GetPod(app.Namespace, app.Name, c.k8sCli)
	if err != nil {
		return errors.Trace(err)
	}

	app.Host = pod.Status.PodIP
	for _, c := range pod.Status.ContainerStatuses {
		app.ContainerIDs = append(app.ContainerIDs, c.ContainerID)
	}

	log.Debugf("db addr:%s", app.Host)

	return nil
}
