package platformsetting

import (
	"context"
	"fmt"
	"github.com/pingcap/log"
	"github.com/pingcap/tep/db/entity"
	"github.com/pingcap/tep/pkg/apis/apiserver"
	"github.com/pingcap/tep/pkg/i18n"
	"github.com/pingcap/tep/pkg/rbac"
	store "github.com/pingcap/tep/pkg/registry/apiserver"
	"github.com/pingcap/tep/pkg/utils"
	"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/registry/rest"
	"time"
)

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

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

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

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

func (s *Storage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
	platform, err := s.GetPlatform(name)
	if err != nil {
		return nil, errors.NewInternalError(err)
	}
	return platform, nil
}

func (s *Storage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
	startTime := time.Now()
	operatorName := utils.GetAPIUsername(ctx)
	if !rbac.CanOperate(s.store.DB, operatorName, rbac.PlatformOperation) {
		return nil, false, errors.NewForbidden(s.resource, operatorName, fmt.Errorf("permission denied"))
	}
	if name != store.UserSettingsPath {
		return nil, false, errors.NewBadRequest("can not update this platform setting")
	}
	platform, err := s.GetPlatform(name)
	if err != nil {
		return nil, false, errors.NewInternalError(err)
	}
	if platform == nil {
		return nil, false, errors.NewBadRequest("platform setting is not found")
	}
	newObj, err := objInfo.UpdatedObject(ctx, platform)
	if err != nil {
		return nil, false, err
	}
	obj, created, err := s.UpdatePlatform(newObj.(*apiserver.PlatformSetting))
	if err == nil {
		if err := s.store.AddEvent(&utils.Event{
			UserID:         operatorName,
			Payload:        newObj,
			OperationType:  entity.UpdateOperation,
			ManagementType: entity.PlatformManagement,
			CostTime:       time.Now().Sub(startTime).Nanoseconds(),
		}); err != nil {
			log.Warn("add operation event failed")
		}
		i18n.PlatformMessage.AddPlatformMessageAndLogError(s.store.DB, i18n.OperateMessageType, i18n.UpdatePlatformSetting)
	}
	return obj, created, err
}

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