package apikey

import (
	"context"
	"fmt"

	"github.com/tidbcloud/account/internel/svc/comsvc"

	apikey2 "github.com/tidbcloud/account/internel/svc/apikey"
	"github.com/tidbcloud/account/internel/svc/user"

	"github.com/tidbcloud/account/internel/dao/entity"

	"github.com/tidbcloud/account/central/utils"
	"github.com/tidbcloud/account/idl/pbgen/proto/central"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type apiKeyService struct {
	central.UnimplementedApiKeyServiceServer
}

func NewService() central.ApiKeyServiceServer {
	return &apiKeyService{}
}

func (s *apiKeyService) CreateApiKeyForMsp(ctx context.Context, req *central.CreateApiKeyForMspReq) (*central.CreateApiKeyForMspResp, error) {
	userSvc, _ := user.GetUserSvc()
	inviteToken, err := userSvc.ValidateUserInviteTokenIgnoreEmail(ctx, req.Token, true)
	if err != nil {
		tilog.Info(ctx, "ValidateUserInviteToken", zap.Any("inviteToken", req.Token), zap.Error(err))
		return nil, err
	}
	tilog.Info(ctx, "ValidateUserInviteToken", zap.Any("inviteToken", inviteToken))

	description := fmt.Sprintf("the first key for %d", inviteToken.TenantID)
	source := "account_ops_msp"
	param := apikey2.CreateApiKeyParam{
		Source:       source,
		Description:  description,
		ScopeType:    string(entity.RoleBindingScopeOrg),
		ResourceIds:  []uint64{inviteToken.TenantID.Uint64()},
		Role:         entity.RoleOrgOwner,
		IgnoreDBuser: true,
		//Operator:    inviteToken.Email,

	}
	operator := comsvc.Operator{
		OpTye: entity.RoleBindingTargetUser,
		OpID:  inviteToken.Operator.Uint64(),
		OpVal: inviteToken.Email,
	}
	apiKey, err := createApiKey(ctx, param, &operator)
	if err != nil {
		tilog.Error(ctx, "CreateApiKey err", zap.Any("err", err), zap.Any("param", param))
		return nil, err
	}
	tilog.Info(ctx, "CreateApiKeyForMsp success", zap.Any("inviteToken", req.Token), zap.Any("apiKey", apiKey.AccessKey), zap.Any("apiKeyId", apiKey.Id))
	return &central.CreateApiKeyForMspResp{AccessKey: apiKey.AccessKey, SecretKey: apiKey.SecretKey}, nil

}

func createApiKey(ctx context.Context, param apikey2.CreateApiKeyParam, operator *comsvc.Operator) (*apikey2.OneConsoleApiKey, error) {
	tilog.Info(ctx, "central create api key start", zap.Any("param", param))
	if param.Description == "" {
		return nil, utils.ConvertErr2GrpcErr(errs.NewBadRequest("description is empty", nil))
	}
	if len(param.Description) > 500 {
		return nil, utils.ConvertErr2GrpcErr(errs.NewBadRequest("description's length exceeded", nil))
	}
	if param.Source == "" {
		return nil, utils.ConvertErr2GrpcErr(errs.NewBadRequest("source is empty", nil))
	}
	if len(param.Source) > 45 {
		return nil, utils.ConvertErr2GrpcErr(errs.NewBadRequest("source's length exceeded", nil))
	}
	if !apikey2.IsValidApiKeyRole(param.ScopeType, param.Role) {
		return nil, utils.ConvertErr2GrpcErr(errs.NewBadRequest("role invalid", nil))
	}
	svc, err := apikey2.GetApiKeySvc()
	if err != nil {
		tilog.Error(ctx, "GetApiKeySvc err", zap.Any("err", err), zap.Any("param", param))
		return nil, err
	}

	keyParam := &apikey2.CreateOneConsoleApiKeyParam{
		Attrs: &apikey2.ApiKeyAttr{
			Source:      param.Source,
			Type:        entity.ApiKeyTypeNormalKey,
			Description: param.Description,
		},
		AssignRoleParam: &comsvc.AssignIdentRolesParam{
			OrgID: param.ResourceIds[0],
			ORole: &param.Role,
		},
	}

	return svc.CreateOneConsoleApiKey(ctx, keyParam, operator)

}
