package restartpod

import (
	"context"
	"fmt"
	"github.com/pingcap/log"
	"github.com/pingcap/tep/db/entity"
	"github.com/pingcap/tep/pkg/apis/apiserver"
	store "github.com/pingcap/tep/pkg/registry/apiserver"
	"github.com/pingcap/tep/pkg/utils"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/registry/rest"
	"strings"
	"time"
)

const podRestartAnnotationName = "tidb.pingcap.com/pod-defer-deleting"

type Storage struct {
	store         *store.Store
	groupResource schema.GroupResource
}

func NewStorage(store *store.Store, groupResource schema.GroupResource) *Storage {
	return &Storage{
		store:         store,
		groupResource: groupResource,
	}
}

func (s *Storage) New() runtime.Object {
	return &apiserver.RestartPod{}
}

func (s *Storage) Kind() string {
	return "RestartPod"
}

func (s *Storage) NamespaceScoped() bool {
	return true
}

func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
	startTime := time.Now()
	operatorName := utils.GetAPIUsername(ctx)
	namespace := request.NamespaceValue(ctx)
	restartPod := obj.(*apiserver.RestartPod)
	if strings.TrimSpace(restartPod.PodName) == "" {
		return nil, errors.NewBadRequest("missing pod name")
	}
	pod, err := s.store.Controller().GetPod(restartPod.PodName, namespace)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil, errors.NewNotFound(s.groupResource, restartPod.PodName)
		}
		msg := fmt.Errorf("failed to get pod[%s]: %v", restartPod.PodName, err)
		return nil, errors.NewInternalError(msg)
	}
	pod.Annotations[podRestartAnnotationName] = ""

	log.Info("restart tidb cluster pod", zap.String("instance", namespace), zap.String("pod", restartPod.PodName))

	_, err = s.store.Controller().UpdatePod(pod, namespace)
	if err != nil {
		msg := fmt.Errorf("failed to  restart pod. %s err: %v", restartPod.PodName, err)
		log.Error("failed to restart pod", zap.String("Name", restartPod.PodName), zap.Error(msg))
		return nil, errors.NewInternalError(msg)
	}
	utils.AddEventAndLogError(s.store.DB, &utils.Event{
		UserID:         operatorName,
		Payload:        fmt.Sprintf("restart %s pod %s", namespace, restartPod.PodName),
		OperationType:  entity.OperateOperation,
		ManagementType: entity.InstanceManagement,
		CostTime:       time.Now().Sub(startTime).Nanoseconds(),
	})
	return restartPod, nil
}
