package cluster

import (
	"context"
	"strconv"

	"github.com/aws/aws-sdk-go-v2/aws/arn"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

const restoreKmsUnavailableErrorMsg = "Restore failed because the configured KMS key is unavailable or inaccessible."

func (s *serviceImpl) validateEncryptionKeyAvailableForRestore(
	ctx context.Context,
	key *entity.EncryptionKey,
	targetSharedPoolID string,
) bizerr.BizError {
	if key == nil {
		return bizerr.New(bizerr.ErrCodeParams, "source cluster encryption key is not found")
	}
	if targetSharedPoolID == "" {
		return bizerr.New(bizerr.ErrCodeParams, "target shared pool id is required to validate source cluster encryption key")
	}
	if key.MasterKey == "" {
		return bizerr.New(bizerr.ErrCodeParams, "%s", restoreKmsUnavailableErrorMsg)
	}

	region, err := s.regionRepo.Get(ctx, key.RegionId)
	if err != nil {
		return bizerr.WrapError(err, "failed to get encryption key region")
	}

	checkReq := &regionalserver.CheckKmsRequest{
		SharedPoolId: targetSharedPoolID,
		KeyId:        key.MasterKey,
	}
	switch key.ProviderName {
	case globalcommonv1.CloudProvider_AWS:
		parsedArn, parseErr := arn.Parse(key.MasterKey)
		if parseErr != nil || parsedArn.Service != "kms" {
			return bizerr.New(bizerr.ErrCodeParams, "%s", restoreKmsUnavailableErrorMsg)
		}
		checkReq.Provider = "aws"
		checkReq.KeyId = parsedArn.String()
		checkReq.Region = parsedArn.Region
		if key.CustomerManaged {
			checkReq.EncryptionContext = map[string]string{
				"ExternalId": strconv.FormatUint(key.TenantId, 10),
			}
		}
	case globalcommonv1.CloudProvider_ALICLOUD:
		checkReq.Provider = "alicloud"
		checkReq.Region = region.GetId().String()
	default:
		return bizerr.New(bizerr.ErrCodeParams, "unsupported cloud provider for source cluster encryption key: %s", key.ProviderName.String())
	}

	if err := s.regionalCli.CheckKms(ctx, region, checkReq); err != nil {
		return restoreKmsCheckFailureError(err)
	}
	return nil
}

func restoreKmsCheckFailureError(err bizerr.BizError) bizerr.BizError {
	if err == nil {
		return nil
	}
	if err.ErrCode() != bizerr.ErrCodeParams {
		return err.Chain("restore kms validation failed")
	}
	return bizerr.New(bizerr.ErrCodeParams, "%s", restoreKmsUnavailableErrorMsg).Chain("%s", err.Error())
}
