package checker

import (
	"context"
	"strconv"
	"strings"

	"github.com/samber/lo"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/apimiddleware/bizerror"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

type cmekId interface {
	GetCustomerManagedEncryptionKeyId() string
}

func CmekIdChecker(
	ctx context.Context,
	checkCtx *CheckContext,
	req any,
	_ *grpc.UnaryServerInfo,
) (context.Context, any, error) {
	reqMeta, err := metadata.ExtractRequestMeta(ctx)
	if err != nil {
		tilog.Error(ctx, "failed to extract request metadata", zap.Error(err))
		return ctx, req, bizerror.NewInternal(err, "failed to extract request metadata")
	}

	cmekReq, ok := req.(cmekId)
	if !ok {
		return ctx, req, bizerr.New(bizerr.ErrCodeParams, "invalid request: req=%v", req)
	}
	cmekIdStr := cmekReq.GetCustomerManagedEncryptionKeyId()

	if cmekIdStr == "" {
		return ctx, req, bizerr.New(bizerr.ErrCodeParams, "cmek id is empty")
	}

	cId := strings.TrimPrefix(cmekIdStr, "cmeks/")

	cIdUint, err := strconv.ParseUint(cId, 10, 64)
	if err != nil {
		return ctx, req, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "invalid cmek id %s", cmekIdStr)
	}

	resp, err := checkCtx.DedicatedCli.GetCustomerManagedEncryptionKey(ctx, &globaldedicatedv1.GetCustomerManagedEncryptionKeyRequest{
		CustomerManagedEncryptionKeyId: cIdUint,
	})
	if err != nil {
		return ctx, req, bizerr.WrapGrpcError(err, "failed to get cmek")
	}

	// Check permission
	if err := reqMeta.CheckProjectIDsPermission(resp.GetProjectId()); err != nil {
		return ctx, req, bizerr.WrapErrorWithCode(err, bizerr.ErrCodePermission, bizerr.ErrMsgResourceNoPermission)
	}

	reqMeta.IsCheckPermission = lo.ToPtr(true)
	reqMeta.DefaultProjectID = resp.GetProjectId()

	return metadata.AppendToIncomingMeta(ctx, reqMeta), req, nil
}
