package gcp

import (
	"context"
	"fmt"
	"path"
	"strconv"
	"strings"

	awsiamv1beta1 "github.com/crossplane-contrib/provider-aws/apis/iam/v1beta1"
	xpawsv1beta1 "github.com/crossplane-contrib/provider-aws/apis/v1beta1"
	computev1alpha1 "github.com/crossplane-contrib/provider-gcp/apis/compute/v1alpha1"
	computev1beta1 "github.com/crossplane-contrib/provider-gcp/apis/compute/v1beta1"
	containerv1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	containerv1beta2 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta2"
	gcpiamv1alpha1 "github.com/crossplane-contrib/provider-gcp/apis/iam/v1alpha1"
	storagev1alpha1 "github.com/crossplane-contrib/provider-gcp/apis/storage/v1alpha1"
	xpgcpv1beta1 "github.com/crossplane-contrib/provider-gcp/apis/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	vpcpeeringv1beta1 "github.com/crossplane/provider-gcp/apis/vpcpeering/v1beta1"

	xpmeta "github.com/crossplane/crossplane-runtime/pkg/meta"
	"github.com/pingcap/log"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/util/retry"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/client"

	bizerr "github.com/tidbcloud/infra-provider/regional-server/pkg/common/errors"
)

var patchAnnotationTPL = `{ "metadata": {"annotations": {"%s": "%s"}} }`

type Option func(*Service) error

type Service struct {
	// we have some special case when creating VPCPeering CR, need to read kube-apiserver directly.
	kubeClient    client.Client // access kube-apiserver with mgr cache.
	kubeAPIReader client.Reader // access kube-apiserver directly.

	// provider string // value can be `eks` or `gke`
	// region   string // phyical region, value can be `us-west-2`, `us-east-2`, `us-west1`
}

func NewService(kubeClient client.Client, kubeAPIReader client.Reader, opts ...Option) (*Service, error) {
	svc := &Service{
		kubeClient:    kubeClient,
		kubeAPIReader: kubeAPIReader,
	}
	for _, opt := range opts {
		if err := opt(svc); err != nil {
			return nil, err
		}
	}
	return svc, nil
}

func (svc *Service) checkAWSProviderConfigExisted(ctx context.Context, name string) error {
	providerConfig := new(xpawsv1beta1.ProviderConfig)
	providerConfig.Name = name
	if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(providerConfig), providerConfig); err != nil {
		log.Error("get aws providerconfig failed", zap.Any("key", client.ObjectKeyFromObject(providerConfig)), zap.Error(err))
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	return nil
}

func (svc *Service) checkGCPProviderConfigExisted(ctx context.Context, name string) error {
	providerConfig := new(xpgcpv1beta1.ProviderConfig)
	providerConfig.Name = name
	if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(providerConfig), providerConfig); err != nil {
		log.Error("get gcp providerconfig failed", zap.Any("key", client.ObjectKeyFromObject(providerConfig)), zap.Error(err))
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	return nil
}

type PObject[T any] interface {
	client.Object
	*T
}

type DeepCopyable[PT PObject[T], T any] interface {
	DeepCopy() PT
}

// we need a type which:
// 1. implement client.Object interface.
// 2. has a DeepCopy() method which return a copy of itself.
type DeepCopyableObject[PT PObject[T], T any] interface {
	DeepCopyable[PT, T]
	PObject[T]
}

// interface version
// type ObjectDeepCopyable interface {
// 	client.Object
// 	DeepCopy() client.Object
// }

func upsertObject[DT DeepCopyableObject[PT, T], PT PObject[T], T any](ctx context.Context, kubeClient client.Client, kubeAPIReader client.Reader, cr DT) error {
	key := client.ObjectKeyFromObject(cr).String()
	cp := cr.DeepCopy()
	if err := kubeClient.Create(ctx, cr); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			log.Error("create object failed", zap.String("key", key), zap.Error(err))
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		// overwrite the object if existed
		var existed PT = new(T)
		if err := kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(cr), existed); err != nil {
			log.Error("get object failed", zap.Error(err), zap.String("key", key))
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if _, ok := any(existed).(*xpgcpv1beta1.ProviderConfig); !ok {
			if !xpmeta.IsPaused(existed) {
				return bizerr.New(bizerr.ResourceConflict, "resource is not paused")
			}
		}
		// commonly, we may implement the update like following:
		// existed.Annotations =  cr.Annotaions
		// existed.Labels = cr.Labels
		// existed.Spec = cr.Spec
		// existed.Status = cr.Status
		// _ := kubeClient.Updated(ctx, cr)
		// however, we cannot access .Spec and .Status field directly since we use generics here
		// so we set resourceversion and uid on cr and update cr as a workaround
		// set these fields to solve precondition error
		cr.SetResourceVersion(existed.GetResourceVersion())
		cr.SetUID(existed.GetUID())
		if err := kubeClient.Update(ctx, cr); err != nil {
			log.Error("update object failed", zap.String("key", key), zap.Error(err))
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		// set these fields to solve conflict error
		err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			if err := kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(existed), existed); err != nil {
				log.Error("get object failed", zap.Error(err), zap.String("key", key))
				return err
			}
			// set these fields to solve conflict error
			cp.SetResourceVersion(existed.GetResourceVersion())
			cp.SetUID(existed.GetUID())
			if err := kubeClient.Status().Update(ctx, cp); err != nil {
				log.Error("update status failed",
					zap.String("key", key),
					zap.String("resourceversion", cp.GetResourceVersion()),
					zap.Int64("generation", cp.GetGeneration()),
					zap.Error(err))
				return err
			}
			return nil
		})
		if err != nil {
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
	} else {
		err := retry.OnError(retry.DefaultRetry,
			func(err error) bool {
				return apierrors.IsConflict(err) || apierrors.IsNotFound(err)
			},
			func() error {
				var existed PT = new(T)
				if err := kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(cr), existed); err != nil {
					log.Error("get object failed", zap.String("key", key), zap.Error(err))
					return err
				}
				// still need to update status with the given cr for the first creation
				cp.SetResourceVersion(existed.GetResourceVersion())
				cp.SetUID(existed.GetUID())
				if err := kubeClient.Status().Update(ctx, cp); err != nil {
					log.Error("update status failed", zap.String("key", key),
						zap.String("resourceversion", cp.GetResourceVersion()),
						zap.Int64("generation", cp.GetGeneration()),
						zap.Error(err))
					return err
				}
				return nil
			})
		if err != nil {
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
	}
	return nil
}

func (svc *Service) ImportAddress(ctx context.Context, cr *computev1beta1.Address) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*computev1beta1.Address](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert address failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}

	// if err := svc.kubeClient.Create(ctx, cr); err != nil {
	// 	log.Error("create address failed", zap.Any("key", client.ObjectKeyFromObject(cr)), zap.Error(err))
	// 	if !apierrors.IsAlreadyExists(err) {
	// 		return bizerr.New(bizerr.K8SClientError, err.Error())
	// 	}
	// 	// overwrite the object if existed
	// 	status := cr.Status.DeepCopy()
	// 	if err := svc.kubeClient.Update(ctx, cr); err != nil {
	// 		log.Error("update address failed", zap.Error(err))
	// 		return bizerr.New(bizerr.K8SClientError, err.Error())
	// 	}
	// 	cr.Status = *status
	// 	if err := svc.kubeClient.Status().Update(ctx, cr); err != nil {
	// 		log.Error("update address status failed", zap.Error(err))
	// 		return bizerr.New(bizerr.K8SClientError, err.Error())
	// 	}
	// }
	return nil
}

func (svc *Service) CheckAddress(ctx context.Context, key client.ObjectKey) error {
	cr := new(computev1beta1.Address)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	// seem address is never set the networkRef and subnetworkRef
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}

	return nil
}

func (svc *Service) ImportFirewall(ctx context.Context, cr *computev1alpha1.Firewall) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	if err := upsertObject[*computev1alpha1.Firewall](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert firewall failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckFirewall(ctx context.Context, key client.ObjectKey) error {
	cr := new(computev1alpha1.Firewall)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.NetworkRef; ref != nil && ref.Name != "" {
		network := new(computev1beta1.Network)
		network.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(network), network); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(pointer.StringDeref(cr.Spec.ForProvider.Network, ""), xpmeta.GetExternalName(network)) {
			return bizerr.New(bizerr.InternalServerError, "network is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}

	return nil
}

func (svc *Service) ImportNetwork(ctx context.Context, cr *computev1beta1.Network) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*computev1beta1.Network](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert network failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckNetwork(ctx context.Context, key client.ObjectKey) error {
	cr := new(computev1beta1.Network)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportRouter(ctx context.Context, cr *computev1alpha1.Router) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	if err := upsertObject[*computev1alpha1.Router](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert router failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckRouter(ctx context.Context, key client.ObjectKey) error {
	cr := new(computev1alpha1.Router)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.NetworkRef; ref != nil && ref.Name != "" {
		network := new(computev1beta1.Network)
		network.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(network), network); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(pointer.StringDeref(cr.Spec.ForProvider.Network, ""), xpmeta.GetExternalName(network)) {
			return bizerr.New(bizerr.InternalServerError, "network is mismatched")
		}
	}
	for _, nat := range cr.Spec.ForProvider.Nats {
		for _, natip := range nat.NatIps {
			address := new(computev1beta1.Address)
			address.Name = path.Base(natip)
			if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(address), address); err != nil {
				if apierrors.IsNotFound(err) {
					return bizerr.New(bizerr.ResourceNotFound, err.Error())
				}
				return bizerr.New(bizerr.K8SClientError, err.Error())
			}
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportSubnetwork(ctx context.Context, cr *computev1beta1.Subnetwork) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	if err := upsertObject[*computev1beta1.Subnetwork](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert subnetwork failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckSubnetwork(ctx context.Context, key client.ObjectKey) error {
	cr := new(computev1beta1.Subnetwork)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.NetworkRef; ref != nil && ref.Name != "" {
		network := new(computev1beta1.Network)
		network.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(network), network); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(pointer.StringDeref(cr.Spec.ForProvider.Network, ""), xpmeta.GetExternalName(network)) {
			return bizerr.New(bizerr.InternalServerError, "network is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportCluster(ctx context.Context, cr *containerv1beta2.Cluster) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	if err := upsertObject[*containerv1beta2.Cluster](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert cluster failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckCluster(ctx context.Context, key client.ObjectKey) error {
	cr := new(containerv1beta2.Cluster)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.NetworkRef; ref != nil && ref.Name != "" {
		network := new(computev1beta1.Network)
		network.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(network), network); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(pointer.StringDeref(cr.Spec.ForProvider.Network, ""), xpmeta.GetExternalName(network)) {
			return bizerr.New(bizerr.InternalServerError, "network is mismatched")
		}
	}
	if ref := cr.Spec.ForProvider.SubnetworkRef; ref != nil && ref.Name != "" {
		subnetwork := new(computev1beta1.Subnetwork)
		subnetwork.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(subnetwork), subnetwork); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(pointer.StringDeref(cr.Spec.ForProvider.Subnetwork, ""), xpmeta.GetExternalName(subnetwork)) {
			return bizerr.New(bizerr.InternalServerError, "subnetwork is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportNodePool(ctx context.Context, cr *containerv1beta1.NodePool) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*containerv1beta1.NodePool](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert nodepool failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckNodePool(ctx context.Context, key client.ObjectKey) error {
	cr := new(containerv1beta1.NodePool)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	// TODO: check the config?
	if ref := cr.Spec.ForProvider.ClusterRef; ref != nil && ref.Name != "" {
		cluster := new(containerv1beta2.Cluster)
		cluster.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(cluster), cluster); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if !strings.Contains(cr.Spec.ForProvider.Cluster, xpmeta.GetExternalName(cluster)) {
			return bizerr.New(bizerr.InternalServerError, "cluster is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportProviderConfig(ctx context.Context, cr *xpgcpv1beta1.ProviderConfig) error {
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	// TODO: other filed need to change
	if err := upsertObject[*xpgcpv1beta1.ProviderConfig](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert providerconfig failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckProviderConfig(ctx context.Context, key client.ObjectKey) error {
	return nil
}

func (svc *Service) ImportServiceAccountKey(ctx context.Context, cr *gcpiamv1alpha1.ServiceAccountKey) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*gcpiamv1alpha1.ServiceAccountKey](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert serviceaccountkey failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckServiceAccountKey(ctx context.Context, key client.ObjectKey) error {
	cr := new(gcpiamv1alpha1.ServiceAccountKey)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	// check the related secret is already existed
	if ref := cr.Spec.WriteConnectionSecretToReference; ref != nil {
		secret := new(corev1.Secret)
		secret.Name = ref.Name
		secret.Namespace = ref.Namespace
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(secret), secret); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
	}
	if ref := cr.Spec.ForProvider.ServiceAccountRef; ref != nil && ref.Name != "" {
		sa := new(gcpiamv1alpha1.ServiceAccount)
		sa.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(sa), sa); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if pointer.StringDeref(cr.Spec.ForProvider.ServiceAccount, "") != sa.Status.AtProvider.Name {
			return bizerr.New(bizerr.InternalServerError, "serviceaccount is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportServiceAccountPolicy(ctx context.Context, cr *gcpiamv1alpha1.ServiceAccountPolicy) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*gcpiamv1alpha1.ServiceAccountPolicy](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert serviceaccountpolicy failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckServiceAccountPolicy(ctx context.Context, key client.ObjectKey) error {
	cr := new(gcpiamv1alpha1.ServiceAccountPolicy)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.ServiceAccountRef; ref != nil && ref.Name != "" {
		sa := new(gcpiamv1alpha1.ServiceAccount)
		sa.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(sa), sa); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if pointer.StringDeref(cr.Spec.ForProvider.ServiceAccount, "") != sa.Status.AtProvider.Name {
			return bizerr.New(bizerr.InternalServerError, "serviceaccount is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportServiceAccount(ctx context.Context, cr *gcpiamv1alpha1.ServiceAccount) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*gcpiamv1alpha1.ServiceAccount](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert serviceaccount failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckServiceAccount(ctx context.Context, key client.ObjectKey) error {
	cr := new(gcpiamv1alpha1.ServiceAccount)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportBucketPolicyMember(ctx context.Context, cr *storagev1alpha1.BucketPolicyMember) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*storagev1alpha1.BucketPolicyMember](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert bucketpolicymember failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckBucketPolicyMember(ctx context.Context, key client.ObjectKey) error {
	cr := new(storagev1alpha1.BucketPolicyMember)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportPeering(ctx context.Context, cr *vpcpeeringv1beta1.Peering) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change
	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*vpcpeeringv1beta1.Peering](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert peering failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckPeering(ctx context.Context, key client.ObjectKey) error {
	cr := new(vpcpeeringv1beta1.Peering)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkGCPProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportOIDCProvider(ctx context.Context, cr *awsiamv1beta1.OpenIDConnectProvider) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change

	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*awsiamv1beta1.OpenIDConnectProvider](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert oidcprovider failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckOIDCProvider(ctx context.Context, key client.ObjectKey) error {
	cr := new(awsiamv1beta1.OpenIDConnectProvider)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportPolicy(ctx context.Context, cr *awsiamv1beta1.Policy) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change

	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}

	if err := upsertObject[*awsiamv1beta1.Policy](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert policy failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckPolicy(ctx context.Context, key client.ObjectKey) error {
	cr := new(awsiamv1beta1.Policy)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportRolePolicyAttachment(ctx context.Context, cr *awsiamv1beta1.RolePolicyAttachment) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change

	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*awsiamv1beta1.RolePolicyAttachment](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert rolepolicyattachment failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckRolePolicyAttachment(ctx context.Context, key client.ObjectKey) error {
	cr := new(awsiamv1beta1.RolePolicyAttachment)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if ref := cr.Spec.ForProvider.RoleNameRef; ref != nil && ref.Name != "" {
		role := new(awsiamv1beta1.Role)
		role.Name = ref.Name
		if err := svc.kubeAPIReader.Get(ctx, client.ObjectKeyFromObject(role), role); err != nil {
			if apierrors.IsNotFound(err) {
				return bizerr.New(bizerr.ResourceNotFound, err.Error())
			}
			return bizerr.New(bizerr.K8SClientError, err.Error())
		}
		if pointer.StringDeref(&cr.Spec.ForProvider.RoleName, "") != xpmeta.GetExternalName(role) {
			return bizerr.New(bizerr.InternalServerError, "rolename is mismatched")
		}
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) ImportRole(ctx context.Context, cr *awsiamv1beta1.Role) error {
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if !cr.DeletionTimestamp.IsZero() {
		return bizerr.New(bizerr.ParameterInvalid, "deletiontimestamp has value")
	}
	if cr.Annotations == nil {
		cr.Annotations = map[string]string{}
	}
	cr.Annotations[xpmeta.AnnotationKeyReconciliationPaused] = "true"
	// the OwnerReferences must be nil, otherwise the resource will be delete automatically
	// since uid in the OwnerReferences cannot match to real resource.
	cr.OwnerReferences = nil
	// TODO: other filed need to change

	// existed cr may used different providerconfig, check the referenced providerconfig is existed
	if cr.Spec.ProviderConfigReference == nil {
		return bizerr.New(bizerr.ParameterInvalid, "providerconfigreference is nil")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	if err := upsertObject[*awsiamv1beta1.Role](ctx, svc.kubeClient, svc.kubeAPIReader, cr); err != nil {
		log.Error("upsert role failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}

func (svc *Service) CheckRole(ctx context.Context, key client.ObjectKey) error {
	cr := new(awsiamv1beta1.Role)
	if err := svc.kubeAPIReader.Get(ctx, key, cr); err != nil {
		if apierrors.IsNotFound(err) {
			return bizerr.New(bizerr.ResourceNotFound, err.Error())
		}
		return bizerr.New(bizerr.K8SClientError, err.Error())
	}
	if xpmeta.GetExternalName(cr) == "" {
		return bizerr.New(bizerr.ParameterInvalid, "external name is empty")
	}
	if err := svc.checkAWSProviderConfigExisted(ctx, cr.Spec.ProviderConfigReference.Name); err != nil {
		return err
	}
	for _, c := range cr.Status.Conditions {
		if c.Type == xpv1.TypeSynced && c.Reason == xpv1.ReasonReconcileError {
			return bizerr.New(bizerr.InternalServerError, fmt.Sprintf("xp resource %s sync failed", cr.Name))
		}
	}
	return nil
}

func (svc *Service) PauseReconcile(ctx context.Context, cr client.Object, pause bool) error {
	patch := []byte(fmt.Sprintf(patchAnnotationTPL,
		xpmeta.AnnotationKeyReconciliationPaused,
		strconv.FormatBool(pause)))
	if err := svc.kubeClient.Patch(context.Background(), cr, client.RawPatch(types.MergePatchType, patch)); err != nil {
		log.Error("patch cr pause annotation failed", zap.String("key", client.ObjectKeyFromObject(cr).String()), zap.Error(err))
		return err
	}
	return nil
}
