package testtoken

import (
	"context"

	"github.com/gogo/protobuf/types"
	"github.com/pingcap/dbaas/central/config"
	"github.com/pingcap/dbaas/central/model/testtoken"
	central "github.com/pingcap/dbaas/central/proto/output"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/tidbcloud/config-service/sdk"
	"go.uber.org/zap"
)

type testTokenService struct {
	model testtoken.Model
}

var (
	_ central.TestTokenServiceServer = (*testTokenService)(nil)

	isUnitTest                            = false
	configService testtoken.ConfigService = nil
)

// NewService constructs a tenantService
func NewService(model testtoken.Model) central.TestTokenServiceServer {
	return &testTokenService{model}
}

func (s *testTokenService) GenerateTestToken(ctx context.Context, req *central.GenerateTestTokenReq) (*central.GenerateTestTokenResp, error) {
	t, err := s.model.GenerateTestToken(req.Auth0Domain, req.Auth0ClientId, req.Auth0ClientSecret, req.Email)
	if err != nil {
		return nil, err
	}
	return &central.GenerateTestTokenResp{
		Token:    t.Token,
		ExpireAt: t.ExpireTime,
	}, nil
}

func (s *testTokenService) GetTenantCustomizedImage(_ context.Context, req *central.GetTenantCustomizedImageReq) (*central.CustomizedTenantImage, error) {
	resp := central.CustomizedTenantImage{}

	stage := config.Global().ServerConfig.Stage
	log.Info("current stage is", zap.String("stage", stage))
	if !(stage == string(config.StageE2E) || stage == string(config.StageNightly) || isUnitTest) {
		return &resp, errors.NotValidf("permission denied")
	}

	if !isUnitTest {
		// unit test will override this variable in TestMain
		// but we can't initialize configService as global variable, because config.Context
		// is initialized in central/cmd/server/main.go InitConfig()...
		configService = sdk.New(config.Context.ConfigServiceURL())
	}

	customizedImage, err := s.model.GetCustomizedImage(req.TenantId, configService)
	if err != nil {
		return &resp, errors.Annotate(err, "failed to get customized image info")
	}

	var (
		TiKVImage    = central.Image{BaseUrl: customizedImage.TiKVImageURL, Tag: customizedImage.TiKVImageTag}
		TiDBImage    = central.Image{BaseUrl: customizedImage.TiDBImageURL, Tag: customizedImage.TiDBImageTag}
		TiFlashImage = central.Image{BaseUrl: customizedImage.TiFlashImageURL, Tag: customizedImage.TiFlashImageTag}
		PDImage      = central.Image{BaseUrl: customizedImage.PDImageTag, Tag: customizedImage.PDImageTag}
		BackupImage  = central.Image{BaseUrl: customizedImage.BackupImageURL, Tag: customizedImage.BackupImageTag}
		RestoreImage = central.Image{BaseUrl: customizedImage.RestoreImageURL, Tag: customizedImage.RestoreImageTag}
	)

	resp = central.CustomizedTenantImage{
		TenantId:       req.TenantId,
		TikvImage:      &TiKVImage,
		TidbImage:      &TiDBImage,
		TiflashImage:   &TiFlashImage,
		PdImage:        &PDImage,
		BackupImage:    &BackupImage,
		RestoreImage:   &RestoreImage,
		DefaultVersion: customizedImage.GlobalVersion,
	}

	log.Info("return config")
	return &resp, nil
}

func (s *testTokenService) SetTenantCustomizedImage(ctx context.Context, req *central.CustomizedTenantImage) (*types.Empty, error) {
	resp := types.Empty{}
	log.Info("trying to set tenant level customized image")

	if !isPermitToCRUDConfigService(req.TenantId) {
		log.Info("the given tenant do not have permission to set customized image", zap.Uint64("tenant_id", req.TenantId))
		return &resp, errors.NotValidf("permission denied")
	}

	if req == nil || req.TikvImage == nil || req.TidbImage == nil || req.TiflashImage == nil || req.BackupImage == nil || req.RestoreImage == nil {
		return &resp, errors.NotValidf("bad request")
	}

	image := testtoken.CustomizedImage{
		TiKVImageURL:    req.TikvImage.BaseUrl,
		TiKVImageTag:    req.TikvImage.Tag,
		TiDBImageURL:    req.TidbImage.BaseUrl,
		TiDBImageTag:    req.TidbImage.Tag,
		TiFlashImageURL: req.TiflashImage.BaseUrl,
		TiFlashImageTag: req.TiflashImage.Tag,
		PDImageURL:      req.PdImage.BaseUrl,
		PDImageTag:      req.PdImage.Tag,
		BackupImageURL:  req.BackupImage.BaseUrl,
		BackupImageTag:  req.BackupImage.Tag,
		RestoreImageURL: req.RestoreImage.BaseUrl,
		RestoreImageTag: req.RestoreImage.Tag,
		GlobalVersion:   req.DefaultVersion,
	}

	if err := s.model.SetCustomizedImage(image, req.TenantId); err != nil {
		log.Error("failed to create cluster config", zap.Error(err))
		return &resp, err
	}

	return &resp, nil
}

func (s *testTokenService) DeleteTenantCustomizedImage(ctx context.Context, req *central.DeleteTenantCustomizedImageReq) (*types.Empty, error) {
	resp := types.Empty{}

	if !isPermitToCRUDConfigService(req.TenantId) {
		return &resp, errors.NotValidf("permission denied")
	}

	err := s.model.DeleteCustomizedImage(req.TenantId)
	if err != nil {
		log.Error("failed to delete tenant customized image", zap.Error(err), zap.Uint64("tenant_id", req.TenantId))
		return &resp, err
	}

	return &resp, nil
}
