package controller

import (
	"fmt"
	"github.com/pingcap/tep/pkg/apis/apiserver"
	"github.com/pingcap/tep/pkg/operator"
	"github.com/pingcap/tep/pkg/utils/alert"
	"github.com/satori/go.uuid"
	"gopkg.in/yaml.v2"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"strings"
)

func (c *Controller) CreateSecret(name string, namespace string, userMap map[string][]byte) error {
	// make sure namespace exists
	err := c.CreateNamespace(namespace)
	if err != nil {
		return err
	}

	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Data: userMap,
		Type: corev1.SecretTypeOpaque,
	}

	if _, err = c.kubeCli.CoreV1().Secrets(namespace).Create(secret); err != nil {
		return err
	}
	return nil
}

func (c *Controller) UpdateSecret(name string, namespace string, userMap map[string][]byte) error {
	var err error
	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
		Data: userMap,
		Type: corev1.SecretTypeOpaque,
	}

	if _, err = c.kubeCli.CoreV1().Secrets(namespace).Update(secret); err != nil {
		return err
	}
	return nil
}

func (c *Controller) GetSecret(name, namespace string) (*corev1.Secret, error) {
	return c.kubeCli.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
}

func (c *Controller) DeleteSecret(name, namespace string) error {
	_, err := c.GetSecret(name, namespace)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}

	err = c.kubeCli.CoreV1().Secrets(namespace).Delete(name, &metav1.DeleteOptions{})
	return err
}

func (c *Controller) getTiDBSuperUserPassword(namespace string) (string, error) {
	secret, err := c.GetSecret(operator.TiDBSecretName, namespace)
	if err != nil {
		return "", err
	}

	password, exist := secret.Data[operator.TiDBSuperUser]
	if !exist {
		return "", fmt.Errorf("user[%s] not found in secret", operator.TiDBSuperUser)
	}
	return string(password), nil
}

func (c *Controller) CreateTiDBSecret(instance *apiserver.TiDBCluster) error {
	userMap := make(map[string][]byte)
	backupUserMap := make(map[string][]byte)

	tidbSuperUserPassword := uuid.NewV4().String()
	userMap["root"] = []byte(strings.TrimSpace(instance.Password))
	userMap[operator.TiDBSuperUser] = []byte(tidbSuperUserPassword)

	backupUserMap["user"] = []byte(operator.TiDBSuperUser)
	backupUserMap["password"] = []byte(tidbSuperUserPassword)

	if err := c.CreateSecret(operator.TiDBSecretName, instance.Name, userMap); err != nil {
		return fmt.Errorf("failed to create tidb secret, TiDBClusterID[%s]. err: %v", instance.Name, err)
	}

	if err := c.CreateSecret(operator.TiDBBackupSecretName, instance.Name, backupUserMap); err != nil {
		return fmt.Errorf("failed to create tidb backup secret, TiDBClusterID[%s]: %s. err: %v", instance.Name, err)
	}
	return nil
}

func (c *Controller) DeleteTiDBSecret(instanceName string) error {
	if err := c.DeleteSecret(operator.TiDBSecretName, instanceName); err != nil {
		return fmt.Errorf("failed to delete tidb secret, TiDBClusterID[%s]. err: %v", instanceName, err)
	}

	if err := c.DeleteSecret(operator.TiDBBackupSecretName, instanceName); err != nil {
		return fmt.Errorf("failed to delete tidb backup secret, TiDBClusterID[%s]. err: %v", instanceName, err)
	}
	return nil
}

const AlertManagerFileName = "alertmanager.yaml"

func (c *Controller) GetAlertmanagerConfig(name string, namespace string) (*alert.Config, error) {
	secret, err := c.GetSecret(name, namespace)
	if err != nil {
		return nil, err
	}

	conf, ok := secret.Data[AlertManagerFileName]
	if !ok {
		return nil, errors.NewInternalError(fmt.Errorf("get alertmanager config failed"))
	}

	var cfg alert.Config
	err = yaml.Unmarshal(conf, &cfg)
	if err != nil {
		return nil, err
	}

	return &cfg, err
}

func (c *Controller) AlertmanagerReload(cfg *alert.Config, alertSecretName string, namespace string) error {
	data, err := yaml.Marshal(cfg)
	if err != nil {
		return err
	}

	if err := c.DeleteSecret(alertSecretName, namespace); err != nil {
		return err
	}

	return c.CreateSecret(alertSecretName, namespace, map[string][]byte{
		AlertManagerFileName: data,
	})
}
