package connectivity

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"

	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"

	util "github.com/alibabacloud-go/tea-utils/service"
	utilV2 "github.com/alibabacloud-go/tea-utils/v2/service"

	ossclient "github.com/alibabacloud-go/alibabacloud-gateway-oss/client"
	gatewayclient "github.com/alibabacloud-go/alibabacloud-gateway-sls/client"
	roaCS "github.com/alibabacloud-go/cs-20151215/v5/client"
	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	roa "github.com/alibabacloud-go/tea-roa/client"
	rpc "github.com/alibabacloud-go/tea-rpc/client"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/adb"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/alikafka"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/bssopenapi"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/cbn"
	cdn_new "github.com/aliyun/alibaba-cloud-sdk-go/services/cdn"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/cloudapi"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/cms"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/cr"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/cr_ee"
	officalCS "github.com/aliyun/alibaba-cloud-sdk-go/services/cs"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ddosbgp"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ddoscoo"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/dds"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/drds"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/edas"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/elasticsearch"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/emr"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ess"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/gpdb"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/hbase"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/market"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/maxcompute"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ots"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/polardb"
	r_kvstore "github.com/aliyun/alibaba-cloud-sdk-go/services/r-kvstore"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ram"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/slb"
	slsPop "github.com/aliyun/alibaba-cloud-sdk-go/services/sls"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/smartag"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/yundun_dbaudit"
	"github.com/aliyun/aliyun-datahub-sdk-go/datahub"
	sls "github.com/aliyun/aliyun-log-go-sdk"
	ali_mns "github.com/aliyun/aliyun-mns-go-sdk"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
	otsTunnel "github.com/aliyun/aliyun-tablestore-go-sdk/tunnel"
	"github.com/aliyun/fc-go-sdk"
	"github.com/denverdino/aliyungo/cdn"
	"github.com/denverdino/aliyungo/cs"

	"github.com/aliyun/alibaba-cloud-sdk-go/services/cassandra"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/dcdn"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/eci"
)

type AliyunClient struct {
	Region               Region
	RegionId             string
	SourceIp             string
	SecureTransport      string
	skipRegionValidation bool
	//In order to build ots table client, add accesskey and secretkey in aliyunclient temporarily.
	AccessKey                    string
	SecretKey                    string
	SecurityToken                string
	OtsInstanceName              string
	accountIdMutex               sync.RWMutex
	config                       *Config
	teaSdkConfig                 rpc.Config
	teaRoaSdkConfig              roa.Config
	teaRpcOpenapiConfig          openapi.Config
	teaRoaOpenapiConfig          openapi.Config
	accountId                    string
	ecsconn                      *ecs.Client
	essconn                      *ess.Client
	vpcconn                      *vpc.Client
	slbconn                      *slb.Client
	alikafkaconn                 *alikafka.Client
	ossconn                      *oss.Client
	dnsconn                      *alidns.Client
	ramconn                      *ram.Client
	csconn                       *cs.Client
	officalCSConn                *officalCS.Client
	roaCSConn                    *roaCS.Client
	cdnconn_new                  *cdn_new.Client
	crconn                       *cr.Client
	creeconn                     *cr_ee.Client
	cdnconn                      *cdn.CdnClient
	otsconn                      *ots.Client
	cmsconn                      *cms.Client
	logconn                      *sls.Client
	fcconn                       *fc.Client
	cenconn                      *cbn.Client
	logpopconn                   *slsPop.Client
	ddsconn                      *dds.Client
	gpdbconn                     *gpdb.Client
	stsconn                      *sts.Client
	rkvconn                      *r_kvstore.Client
	polarDBconn                  *polardb.Client
	dhconn                       datahub.DataHubApi
	mnsconn                      *ali_mns.MNSClient
	cloudapiconn                 *cloudapi.Client
	teaConn                      *rpc.Client
	tablestoreconnByInstanceName map[string]*tablestore.TableStoreClient
	otsTunnelConnByInstanceName  map[string]otsTunnel.TunnelClient
	csprojectconnByKey           map[string]*cs.ProjectClient
	drdsconn                     *drds.Client
	elasticsearchconn            *elasticsearch.Client
	ddoscooconn                  *ddoscoo.Client
	ddosbgpconn                  *ddosbgp.Client
	bssopenapiconn               *bssopenapi.Client
	emrconn                      *emr.Client
	sagconn                      *smartag.Client
	dbauditconn                  *yundun_dbaudit.Client
	marketconn                   *market.Client
	hbaseconn                    *hbase.Client
	adbconn                      *adb.Client
	cbnConn                      *cbn.Client
	maxcomputeconn               *maxcompute.Client
	dnsConn                      *alidns.Client
	edasconn                     *edas.Client
	bssopenapiConn               *bssopenapi.Client
	alidnsConn                   *alidns.Client
	ddoscooConn                  *ddoscoo.Client
	cassandraConn                *cassandra.Client
	eciConn                      *eci.Client
	ecsConn                      *ecs.Client
	dcdnConn                     *dcdn.Client
	cmsConn                      *cms.Client
	r_kvstoreConn                *r_kvstore.Client
	maxcomputeConn               *maxcompute.Client
}

type ApiVersion string

const (
	ApiVersion20140526 = ApiVersion("2014-05-26")
	ApiVersion20160815 = ApiVersion("2016-08-15")
	ApiVersion20140515 = ApiVersion("2014-05-15")
)

const businessInfoKey = "Terraform"

const DefaultClientRetryCountSmall = 5

const DefaultClientRetryCountMedium = 10

const DefaultClientRetryCountLarge = 15

const Terraform = "HashiCorp-Terraform"

const Provider = "Terraform-Provider"

const Module = "Terraform-Module"

const TerraformTraceId = "TerraformTraceId"

var goSdkMutex = sync.RWMutex{} // The Go SDK is not thread-safe
var loadSdkfromRemoteMutex = sync.Mutex{}
var loadSdkEndpointMutex = sync.Mutex{}

// The main version number that is being run at the moment.
var providerVersion = "1.260.1"

// Temporarily maintain map for old ecs client methods and store special endpoint information
var EndpointMap = map[string]string{
	"cn-shenzhen-su18-b01":        "ecs.aliyuncs.com",
	"cn-beijing":                  "ecs.aliyuncs.com",
	"cn-shenzhen-st4-d01":         "ecs.aliyuncs.com",
	"cn-haidian-cm12-c01":         "ecs.aliyuncs.com",
	"cn-hangzhou-internal-prod-1": "ecs.aliyuncs.com",
	"cn-qingdao":                  "ecs.aliyuncs.com",
	"cn-shanghai":                 "ecs.aliyuncs.com",
	"cn-shanghai-finance-1":       "ecs.aliyuncs.com",
	"cn-hongkong":                 "ecs.aliyuncs.com",
	"us-west-1":                   "ecs.aliyuncs.com",
	"cn-shenzhen":                 "ecs.aliyuncs.com",
	"cn-shanghai-et15-b01":        "ecs.aliyuncs.com",
	"cn-hangzhou-bj-b01":          "ecs.aliyuncs.com",
	"cn-zhangbei-na61-b01":        "ecs.aliyuncs.com",
	"cn-shenzhen-finance-1":       "ecs.aliyuncs.com",
	"cn-shanghai-et2-b01":         "ecs.aliyuncs.com",
	"ap-southeast-1":              "ecs.aliyuncs.com",
	"cn-beijing-nu16-b01":         "ecs.aliyuncs.com",
	"us-east-1":                   "ecs.aliyuncs.com",
	"cn-fujian":                   "ecs.aliyuncs.com",
	"cn-hangzhou":                 "ecs.aliyuncs.com",
}

// Client for AliyunClient
func (c *Config) Client() (*AliyunClient, error) {
	// Get the auth and region. This can fail if keys/regions were not
	// specified and we're attempting to use the environment.
	if !c.SkipRegionValidation {
		err := c.loadAndValidate()
		if err != nil {
			return nil, err
		}
	}
	loadLocalEndpoint = hasLocalEndpoint()
	if hasLocalEndpoint() {
		if err := c.loadEndpointFromLocal(); err != nil {
			return nil, err
		}
	}
	teaSdkConfig, err := c.getTeaDslSdkConfig(true)
	if err != nil {
		return nil, err
	}
	teaRoaSdkConfig, err := c.getTeaRoaDslSdkConfig(true)
	if err != nil {
		return nil, err
	}
	teaRpcOpenapiConfig, err := c.getTeaRpcOpenapiConfig(true)
	if err != nil {
		return nil, err
	}
	teaRoaOpenapiConfig, err := c.getTeaRoaOpenapiConfig(true)
	if err != nil {
		return nil, err
	}
	client := &AliyunClient{
		config:                       c,
		teaSdkConfig:                 teaSdkConfig,
		teaRoaSdkConfig:              teaRoaSdkConfig,
		teaRpcOpenapiConfig:          teaRpcOpenapiConfig,
		teaRoaOpenapiConfig:          teaRoaOpenapiConfig,
		SourceIp:                     c.SourceIp,
		Region:                       c.Region,
		RegionId:                     c.RegionId,
		AccessKey:                    c.AccessKey,
		SecretKey:                    c.SecretKey,
		SecurityToken:                c.SecurityToken,
		OtsInstanceName:              c.OtsInstanceName,
		accountId:                    c.AccountId,
		tablestoreconnByInstanceName: make(map[string]*tablestore.TableStoreClient),
		otsTunnelConnByInstanceName:  make(map[string]otsTunnel.TunnelClient),
		csprojectconnByKey:           make(map[string]*cs.ProjectClient),
		skipRegionValidation:         c.SkipRegionValidation,
	}
	if c.AccountType == "" {
		c.AccountType = client.getAccountType()
		client.config = c
	}
	log.Printf("[INFO] caller identity's account type is %s.", client.config.AccountType)
	return client, nil
}

func (client *AliyunClient) WithEcsClient(do func(*ecs.Client) (interface{}, error)) (interface{}, error) {
	if client.ecsconn != nil && !client.config.needRefreshCredential() {
		return do(client.ecsconn)
	}
	product := "ecs"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	ecsconn, err := ecs.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(time.Duration(60)*time.Second), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the ECS client: %#v", err)
	}
	ecs.SetClientProperty(ecsconn, "EndpointMap", map[string]string{
		client.RegionId: endpoint,
	})
	ecs.SetEndpointDataToClient(ecsconn)

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				ecsconn.SetHttpsProxy(proxy.String())
			} else {
				ecsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	ecsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	ecsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	ecsconn.SourceIp = client.config.SourceIp
	ecsconn.SecureTransport = client.config.SecureTransport
	client.ecsconn = ecsconn
	return do(client.ecsconn)
}

func (client *AliyunClient) WithOfficalCSClient(do func(*officalCS.Client) (interface{}, error)) (interface{}, error) {
	if client.officalCSConn != nil && !client.config.needRefreshCredential() {
		return do(client.officalCSConn)
	}
	product := "cs"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	csconn, err := officalCS.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				csconn.SetHttpsProxy(proxy.String())
			} else {
				csconn.SetHttpProxy(proxy.String())
			}
		}
	}

	csconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	csconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	csconn.SourceIp = client.config.SourceIp
	csconn.SecureTransport = client.config.SecureTransport
	client.officalCSConn = csconn
	return do(client.officalCSConn)
}

func (client *AliyunClient) WithPolarDBClient(do func(*polardb.Client) (interface{}, error)) (interface{}, error) {
	if client.polarDBconn != nil && !client.config.needRefreshCredential() {
		return do(client.polarDBconn)
	}
	product := "polardb"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	polarDBconn, err := polardb.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the PolarDB client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				polarDBconn.SetHttpsProxy(proxy.String())
			} else {
				polarDBconn.SetHttpProxy(proxy.String())
			}
		}
	}

	polarDBconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	polarDBconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	polarDBconn.SourceIp = client.config.SourceIp
	polarDBconn.SecureTransport = client.config.SecureTransport
	client.polarDBconn = polarDBconn
	return do(client.polarDBconn)
}

func (client *AliyunClient) WithSlbClient(do func(*slb.Client) (interface{}, error)) (interface{}, error) {
	if client.slbconn != nil && !client.config.needRefreshCredential() {
		return do(client.slbconn)
	}
	product := "slb"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	slbconn, err := slb.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the SLB client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				slbconn.SetHttpsProxy(proxy.String())
			} else {
				slbconn.SetHttpProxy(proxy.String())
			}
		}
	}

	slbconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	slbconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	slbconn.SourceIp = client.config.SourceIp
	slbconn.SecureTransport = client.config.SecureTransport
	client.slbconn = slbconn
	return do(client.slbconn)
}

func (client *AliyunClient) WithVpcClient(do func(*vpc.Client) (interface{}, error)) (interface{}, error) {
	if client.vpcconn != nil && !client.config.needRefreshCredential() {
		return do(client.vpcconn)
	}
	product := "vpc"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	vpcconn, err := vpc.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(time.Duration(60)*time.Second), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the VPC client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				vpcconn.SetHttpsProxy(proxy.String())
			} else {
				vpcconn.SetHttpProxy(proxy.String())
			}
		}
	}

	vpcconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	vpcconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	vpcconn.SourceIp = client.config.SourceIp
	vpcconn.SecureTransport = client.config.SecureTransport
	client.vpcconn = vpcconn
	return do(client.vpcconn)
}

func (client *AliyunClient) WithEssClient(do func(*ess.Client) (interface{}, error)) (interface{}, error) {
	if client.essconn != nil && !client.config.needRefreshCredential() {
		return do(client.essconn)
	}
	product := "ess"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	essconn, err := ess.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the ESS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				essconn.SetHttpsProxy(proxy.String())
			} else {
				essconn.SetHttpProxy(proxy.String())
			}
		}
	}

	essconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	essconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	essconn.SourceIp = client.config.SourceIp
	essconn.SecureTransport = client.config.SecureTransport
	client.essconn = essconn
	return do(client.essconn)
}

func (client *AliyunClient) WithOssClient(do func(*oss.Client) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	if client.ossconn != nil && !client.config.needRefreshCredential() {
		return do(client.ossconn)
	}
	product := "oss"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	schma := strings.ToLower(client.config.Protocol)
	if !strings.HasPrefix(endpoint, "http") {
		endpoint = fmt.Sprintf("%s://%s", schma, endpoint)
	}

	clientOptions := []oss.ClientOption{oss.UserAgent(client.config.getUserAgent())}
	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}
		if !skip {
			clientOptions = append(clientOptions, oss.Proxy(proxy.String()))
		}
	}

	clientOptions = append(clientOptions, oss.SetCredentialsProvider(&ossCredentialsProvider{client: client}))

	// region
	clientOptions = append(clientOptions, oss.Region(client.config.RegionId))

	// SignVersion
	if ossV, ok := client.config.SignVersion.Load("oss"); ok {
		clientOptions = append(clientOptions, oss.AuthVersion(func(v any) oss.AuthVersionType {
			switch fmt.Sprintf("%v", v) {
			case "v4":
				return oss.AuthV4
			case "v2":
				return oss.AuthV2
			}
			//default is v1
			return oss.AuthV1
		}(ossV)))
	}

	ossconn, err := oss.New(endpoint, "", "", clientOptions...)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the OSS client: %#v", err)
	}

	client.ossconn = ossconn
	return do(client.ossconn)
}

func (client *AliyunClient) WithOssBucketByName(bucketName string, do func(*oss.Bucket) (interface{}, error)) (interface{}, error) {
	return client.WithOssClient(func(ossClient *oss.Client) (interface{}, error) {
		bucket, err := client.ossconn.Bucket(bucketName)
		if err != nil {
			return nil, fmt.Errorf("unable to get the bucket %s: %#v", bucketName, err)
		}
		return do(bucket)
	})
}

func (client *AliyunClient) WithDnsClient(do func(*alidns.Client) (interface{}, error)) (interface{}, error) {
	if client.dnsconn != nil && !client.config.needRefreshCredential() {
		return do(client.dnsconn)
	}
	product := "alidns"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(DNSCode), endpoint)
	}

	dnsconn, err := alidns.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the DNS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				dnsconn.SetHttpsProxy(proxy.String())
			} else {
				dnsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	dnsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	dnsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	dnsconn.SourceIp = client.config.SourceIp
	dnsconn.SecureTransport = client.config.SecureTransport
	client.dnsconn = dnsconn
	return do(client.dnsconn)
}

func (client *AliyunClient) WithRamClient(do func(*ram.Client) (interface{}, error)) (interface{}, error) {
	if client.ramconn != nil && !client.config.needRefreshCredential() {
		return do(client.ramconn)
	}
	product := "ram"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	ramconn, err := ram.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the RAM client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				ramconn.SetHttpsProxy(proxy.String())
			} else {
				ramconn.SetHttpProxy(proxy.String())
			}
		}
	}

	ramconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	ramconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	ramconn.SourceIp = client.config.SourceIp
	ramconn.SecureTransport = client.config.SecureTransport
	client.ramconn = ramconn

	return do(client.ramconn)
}

func (client *AliyunClient) WithCsClient(do func(*cs.Client) (interface{}, error)) (interface{}, error) {
	if client.csconn != nil && !client.config.needRefreshCredential() {
		return do(client.csconn)
	}
	product := "cs"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	endpoint = fmt.Sprintf("https://%s", endpoint)
	accessKey, secretKey, stsToken := client.config.AccessKey, client.config.SecretKey, client.config.SecurityToken
	credential, err := client.config.Credential.GetCredential()
	if err != nil || credential == nil {
		log.Printf("[WARN] get credential failed. Error: %#v", err)
	} else {
		accessKey, secretKey, stsToken = *credential.AccessKeyId, *credential.AccessKeySecret, *credential.SecurityToken
	}

	csconn := cs.NewClientForAussumeRole(accessKey, secretKey, stsToken)
	csconn.SetUserAgent(client.config.getUserAgent())
	csconn.SetEndpoint(endpoint)
	csconn.SetSourceIp(client.config.SourceIp)
	csconn.SetSecureTransport(client.config.SecureTransport)
	client.csconn = csconn
	return do(client.csconn)
}

func (client *AliyunClient) NewRoaCsClient() (*roaCS.Client, error) {
	product := "cs"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	accessKey, secretKey, stsToken := client.config.AccessKey, client.config.SecretKey, client.config.SecurityToken
	credential, err := client.config.Credential.GetCredential()
	if err != nil || credential == nil {
		log.Printf("[WARN] get credential failed. Error: %#v", err)
	} else {
		accessKey, secretKey, stsToken = *credential.AccessKeyId, *credential.AccessKeySecret, *credential.SecurityToken
	}
	header := map[string]*string{
		"x-acs-source-ip":        tea.String(client.config.SourceIp),
		"x-acs-secure-transport": tea.String(client.config.SecureTransport),
	}
	param := &openapi.GlobalParameters{Headers: header}
	// Initialize the CS client if necessary
	roaCSConn, err := roaCS.NewClient(&openapi.Config{
		AccessKeyId:      tea.String(accessKey),
		AccessKeySecret:  tea.String(secretKey),
		SecurityToken:    tea.String(stsToken),
		RegionId:         tea.String(client.config.RegionId),
		UserAgent:        tea.String(client.config.getUserAgent()),
		Endpoint:         tea.String(endpoint),
		ReadTimeout:      tea.Int(client.config.ClientReadTimeout),
		ConnectTimeout:   tea.Int(client.config.ClientConnectTimeout),
		GlobalParameters: param,
	})
	if err != nil {
		return nil, err
	}

	return roaCSConn, nil
}

func (client *AliyunClient) WithCrClient(do func(*cr.Client) (interface{}, error)) (interface{}, error) {
	if client.crconn != nil && !client.config.needRefreshCredential() {
		return do(client.crconn)
	}
	product := "cr"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(CRCode), endpoint)
	}
	crconn, err := cr.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CR client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				crconn.SetHttpsProxy(proxy.String())
			} else {
				crconn.SetHttpProxy(proxy.String())
			}
		}
	}

	crconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	crconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	crconn.SourceIp = client.config.SourceIp
	crconn.SecureTransport = client.config.SecureTransport
	client.crconn = crconn

	return do(client.crconn)
}

func (client *AliyunClient) WithCrEEClient(do func(*cr_ee.Client) (interface{}, error)) (interface{}, error) {
	if client.creeconn != nil && !client.config.needRefreshCredential() {
		return do(client.creeconn)
	}
	product := "cr"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(CRCode), endpoint)
	}
	creeconn, err := cr_ee.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CR EE client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				creeconn.SetHttpsProxy(proxy.String())
			} else {
				creeconn.SetHttpProxy(proxy.String())
			}
		}
	}

	creeconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	creeconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	creeconn.SourceIp = client.config.SourceIp
	creeconn.SecureTransport = client.config.SecureTransport
	client.creeconn = creeconn

	return do(client.creeconn)
}

func (client *AliyunClient) WithCdnClient(do func(*cdn.CdnClient) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the CDN client if necessary
	if client.cdnconn == nil {
		cdnconn := cdn.NewClient(client.config.AccessKey, client.config.SecretKey)
		cdnconn.SetBusinessInfo(businessInfoKey)
		cdnconn.SetUserAgent(client.getUserAgent())
		cdnconn.SetSecurityToken(client.config.SecurityToken)
		endpoint := client.config.CdnEndpoint
		if endpoint == "" {
			endpoint = loadEndpoint(client.config.RegionId, CDNCode)
		}
		if endpoint != "" && !strings.HasPrefix(endpoint, "http") {
			cdnconn.SetEndpoint(fmt.Sprintf("https://%s", strings.TrimPrefix(endpoint, "://")))
		}
		client.cdnconn = cdnconn
	}
	return do(client.cdnconn)
}

func (client *AliyunClient) WithCdnClient_new(do func(*cdn_new.Client) (interface{}, error)) (interface{}, error) {
	if client.cdnconn_new != nil && !client.config.needRefreshCredential() {
		return do(client.cdnconn_new)
	}
	product := "cdn"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(CDNCode), endpoint)
	}
	cdnconn, err := cdn_new.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CDN client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				cdnconn.SetHttpsProxy(proxy.String())
			} else {
				cdnconn.SetHttpProxy(proxy.String())
			}
		}
	}

	cdnconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	cdnconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	cdnconn.SourceIp = client.config.SourceIp
	cdnconn.SecureTransport = client.config.SecureTransport
	client.cdnconn_new = cdnconn

	return do(client.cdnconn_new)
}

// WithOtsClient init ots openapi publish sdk client(if necessary), and exec do func by client
func (client *AliyunClient) WithOtsClient(do func(*ots.Client) (interface{}, error)) (interface{}, error) {
	if client.otsconn != nil && !client.config.needRefreshCredential() {
		return do(client.otsconn)
	}
	product := "ots"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	otsconn, err := ots.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the OTS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				otsconn.SetHttpsProxy(proxy.String())
			} else {
				otsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	otsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	otsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	otsconn.SourceIp = client.config.SourceIp
	otsconn.SecureTransport = client.config.SecureTransport
	client.otsconn = otsconn

	return do(client.otsconn)
}

// NewOtsRoaClient rpc client for common sdk
func (client *AliyunClient) NewOtsRoaClient(productCode string) (*roa.Client, error) {
	// first, load endpoint by user setting
	if v, ok := client.config.Endpoints.Load(productCode); !ok || v.(string) == "" {
		// second,  load endpoint by serverside rule
		if err := client.loadEndpoint(productCode); err != nil {
			return nil, err
		}
	}
	// set endpoint
	endpoint := ""
	if v, ok := client.config.Endpoints.Load(productCode); ok && v.(string) != "" {
		endpoint = v.(string)
	}
	if endpoint == "" {
		return nil, fmt.Errorf("[ERROR] missing the product %s endpoint", productCode)
	}
	sdkConfig := client.teaRoaSdkConfig
	sdkConfig.SetEndpoint(endpoint)
	accessKey, secretKey, securityToken := client.config.GetRefreshCredential()
	sdkConfig.SetAccessKeyId(accessKey)
	sdkConfig.SetAccessKeySecret(secretKey)
	sdkConfig.SetSecurityToken(securityToken)

	conn, err := roa.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the %s client: %#v", productCode, err)
	}

	return conn, nil
}

func (client *AliyunClient) WithCmsClient(do func(*cms.Client) (interface{}, error)) (interface{}, error) {
	if client.cmsconn != nil && !client.config.needRefreshCredential() {
		return do(client.cmsconn)
	}
	product := "cms"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	cmsconn, err := cms.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CMS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				cmsconn.SetHttpsProxy(proxy.String())
			} else {
				cmsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	cmsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	cmsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	cmsconn.SourceIp = client.config.SourceIp
	cmsconn.SecureTransport = client.config.SecureTransport
	client.cmsconn = cmsconn

	return do(client.cmsconn)
}

func (client *AliyunClient) WithLogPopClient(do func(*slsPop.Client) (interface{}, error)) (interface{}, error) {
	if client.logpopconn != nil && !client.config.needRefreshCredential() {
		return do(client.logpopconn)
	}
	product := "sls"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}

	logpopconn, err := slsPop.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the sls client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				logpopconn.SetHttpsProxy(proxy.String())
			} else {
				logpopconn.SetHttpProxy(proxy.String())
			}
		}
	}

	logpopconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	logpopconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	logpopconn.SourceIp = client.config.SourceIp
	logpopconn.SecureTransport = client.config.SecureTransport
	endpoint = strings.TrimPrefix(strings.TrimPrefix(endpoint, "https://"), "http://")
	logpopconn.Domain = endpoint + "/open-api"
	client.logpopconn = logpopconn

	return do(client.logpopconn)
}

func (client *AliyunClient) WithLogClient(do func(*sls.Client) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	if client.logconn != nil && !client.config.needRefreshCredential() {
		return do(client.logconn)
	}
	product := "sls"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}

	if !strings.HasPrefix(endpoint, "http") {
		endpoint = fmt.Sprintf("https://%s", strings.TrimPrefix(endpoint, "://"))
	}
	accessKey, secretKey, securityToken := client.config.GetRefreshCredential()
	client.logconn = &sls.Client{
		AccessKeyID:     accessKey,
		AccessKeySecret: secretKey,
		Endpoint:        endpoint,
		SecurityToken:   securityToken,
		UserAgent:       client.getUserAgent(),
	}

	return do(client.logconn)
}

func (client *AliyunClient) WithDrdsClient(do func(*drds.Client) (interface{}, error)) (interface{}, error) {
	if client.drdsconn != nil && !client.config.needRefreshCredential() {
		return do(client.drdsconn)
	}
	product := "drds"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	drdsconn, err := drds.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the DRDS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				drdsconn.SetHttpsProxy(proxy.String())
			} else {
				drdsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	drdsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	drdsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	drdsconn.SourceIp = client.config.SourceIp
	drdsconn.SecureTransport = client.config.SecureTransport
	client.drdsconn = drdsconn
	return do(client.drdsconn)
}

func (client *AliyunClient) WithDdsClient(do func(*dds.Client) (interface{}, error)) (interface{}, error) {
	if client.ddsconn != nil && !client.config.needRefreshCredential() {
		return do(client.ddsconn)
	}
	product := "dds"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}

	ddsconn, err := dds.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the mongoDB client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				ddsconn.SetHttpsProxy(proxy.String())
			} else {
				ddsconn.SetHttpProxy(proxy.String())
			}
		}
	}

	ddsconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	ddsconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	ddsconn.SourceIp = client.config.SourceIp
	ddsconn.SecureTransport = client.config.SecureTransport
	client.ddsconn = ddsconn

	return do(client.ddsconn)
}

func (client *AliyunClient) WithGpdbClient(do func(*gpdb.Client) (interface{}, error)) (interface{}, error) {
	if client.gpdbconn != nil && !client.config.needRefreshCredential() {
		return do(client.gpdbconn)
	}
	product := "gpdb"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}

	gpdbconn, err := gpdb.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the GPDB client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				gpdbconn.SetHttpsProxy(proxy.String())
			} else {
				gpdbconn.SetHttpProxy(proxy.String())
			}
		}
	}

	gpdbconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	gpdbconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	gpdbconn.SourceIp = client.config.SourceIp
	gpdbconn.SecureTransport = client.config.SecureTransport
	client.gpdbconn = gpdbconn
	return do(client.gpdbconn)
}

func (client *AliyunClient) WithFcClient(do func(*fc.Client) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()
	if client.fcconn != nil && !client.config.needRefreshCredential() {
		return do(client.fcconn)
	}
	product := "fc"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if strings.HasPrefix(endpoint, "http") {
		endpoint = strings.TrimPrefix(strings.TrimPrefix(endpoint, "http://"), "https://")
	}

	config := client.getSdkConfig(0)
	transport := config.HttpTransport
	// Receiving proxy config from environment
	transport.Proxy = http.ProxyFromEnvironment
	clientOptions := []fc.ClientOption{fc.WithSecurityToken(client.config.SecurityToken), fc.WithTransport(transport),
		fc.WithTimeout(30), fc.WithRetryCount(DefaultClientRetryCountSmall)}

	accessKey, secretKey, secretToken := client.config.GetRefreshCredential()
	fcconn, err := fc.NewClient(fmt.Sprintf("https://%s", endpoint), string(ApiVersion20160815), accessKey, secretKey, clientOptions...)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the FC client: %#v", err)
	}

	fcconn.Config.UserAgent = client.config.getUserAgent()
	fcconn.Config.SecurityToken = secretToken
	client.fcconn = fcconn

	return do(client.fcconn)
}

func (client *AliyunClient) WithCloudApiClient(do func(*cloudapi.Client) (interface{}, error)) (interface{}, error) {
	if client.cloudapiconn != nil && !client.config.needRefreshCredential() {
		return do(client.cloudapiconn)
	}
	product := "cloudapi"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.RegionId, product, endpoint)
	}
	cloudapiconn, err := cloudapi.NewClientWithOptions(client.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the CloudAPI client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				cloudapiconn.SetHttpsProxy(proxy.String())
			} else {
				cloudapiconn.SetHttpProxy(proxy.String())
			}
		}
	}

	cloudapiconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	cloudapiconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	cloudapiconn.SourceIp = client.config.SourceIp
	cloudapiconn.SecureTransport = client.config.SecureTransport
	client.cloudapiconn = cloudapiconn

	return do(client.cloudapiconn)
}

func (client *AliyunClient) NewTeaCommonClient(endpoint string) (*rpc.Client, error) {
	sdkConfig := client.teaSdkConfig
	sdkConfig.SetEndpoint(endpoint)
	credential, err := client.config.Credential.GetCredential()
	if err == nil && credential != nil {
		sdkConfig.SetAccessKeyId(*credential.AccessKeyId)
		sdkConfig.SetAccessKeySecret(*credential.AccessKeySecret)
		sdkConfig.SetSecurityToken(client.config.SecurityToken)
	} else {
		log.Printf("get credential error: %#v", err)
	}

	conn, err := rpc.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the tea client: %#v", err)
	}

	return conn, nil
}
func (client *AliyunClient) NewTeaRoaCommonClient(endpoint string) (*roa.Client, error) {
	sdkConfig := client.teaRoaSdkConfig
	sdkConfig.SetEndpoint(endpoint)
	credential, err := client.config.Credential.GetCredential()
	if err == nil && credential != nil {
		sdkConfig.SetAccessKeyId(*credential.AccessKeyId)
		sdkConfig.SetAccessKeySecret(*credential.AccessKeySecret)
		sdkConfig.SetSecurityToken(client.config.SecurityToken)
	} else {
		log.Printf("get credential error: %#v", err)
	}

	conn, err := roa.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the tea roa client: %#v", err)
	}

	return conn, nil
}

func (client *AliyunClient) WithDataHubClient(do func(api datahub.DataHubApi) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the DataHub client if necessary
	if client.dhconn == nil {
		endpoint := client.config.DatahubEndpoint
		if endpoint == "" {
			endpoint = loadEndpoint(client.RegionId, DATAHUBCode)
		}
		if endpoint == "" {
			if client.RegionId == string(APSouthEast1) {
				endpoint = "dh-singapore.aliyuncs.com"
			} else {
				endpoint = fmt.Sprintf("dh-%s.aliyuncs.com", client.RegionId)
			}
		}
		if !strings.HasPrefix(endpoint, "http") {
			endpoint = fmt.Sprintf("https://%s", endpoint)
		}

		account := datahub.NewStsCredential(client.config.AccessKey, client.config.SecretKey, client.config.SecurityToken)
		config := &datahub.Config{
			UserAgent: client.getUserAgent(),
		}

		client.dhconn = datahub.NewClientWithConfig(endpoint, config, account)
	}

	return do(client.dhconn)
}

func (client *AliyunClient) WithElasticsearchClient(do func(*elasticsearch.Client) (interface{}, error)) (interface{}, error) {
	if client.elasticsearchconn != nil && !client.config.needRefreshCredential() {
		return do(client.elasticsearchconn)
	}
	product := "elasticsearch"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	elasticsearchconn, err := elasticsearch.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Elasticsearch client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				elasticsearchconn.SetHttpsProxy(proxy.String())
			} else {
				elasticsearchconn.SetHttpProxy(proxy.String())
			}
		}
	}

	elasticsearchconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	elasticsearchconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	elasticsearchconn.SourceIp = client.config.SourceIp
	elasticsearchconn.SecureTransport = client.config.SecureTransport
	client.elasticsearchconn = elasticsearchconn

	return do(client.elasticsearchconn)
}

func (client *AliyunClient) WithMnsClient(do func(*ali_mns.MNSClient) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the MNS client if necessary
	if client.mnsconn == nil {
		endpoint := client.config.MnsEndpoint
		if endpoint == "" {
			endpoint = loadEndpoint(client.config.RegionId, MNSCode)
			if endpoint == "" {
				endpoint = fmt.Sprintf("%s.aliyuncs.com", client.config.RegionId)
			}
		}

		accountId, err := client.AccountId()
		if err != nil {
			return nil, err
		}
		if strings.HasPrefix(endpoint, "http") {
			endpoint = strings.TrimPrefix(strings.TrimPrefix(endpoint, "http://"), "https://")
		}
		mnsUrl := fmt.Sprintf("https://%s.mns.%s", accountId, endpoint)
		accessKey, secretKey, token := client.config.GetRefreshCredential()
		mnsClient := ali_mns.NewAliMNSClientWithToken(mnsUrl, accessKey, secretKey, token)
		proxy, err := client.getHttpProxy()
		if proxy != nil {
			skip, err := client.skipProxy(endpoint)
			if err != nil {
				return nil, err
			}
			if !skip {
				mnsClient.SetProxy(proxy.String())
			}
		}
		client.mnsconn = &mnsClient
	}

	return do(client.mnsconn)
}
func (client *AliyunClient) WithMnsQueueManager(do func(ali_mns.AliQueueManager) (interface{}, error)) (interface{}, error) {
	return client.WithMnsClient(func(mnsClient *ali_mns.MNSClient) (interface{}, error) {
		queueManager := ali_mns.NewMNSQueueManager(*mnsClient)
		return do(queueManager)
	})
}

func (client *AliyunClient) WithMnsTopicManager(do func(ali_mns.AliTopicManager) (interface{}, error)) (interface{}, error) {
	return client.WithMnsClient(func(mnsClient *ali_mns.MNSClient) (interface{}, error) {
		topicManager := ali_mns.NewMNSTopicManager(*mnsClient)
		return do(topicManager)
	})
}

func (client *AliyunClient) WithMnsSubscriptionManagerByTopicName(topicName string, do func(ali_mns.AliMNSTopic) (interface{}, error)) (interface{}, error) {
	return client.WithMnsClient(func(mnsClient *ali_mns.MNSClient) (interface{}, error) {
		subscriptionManager := ali_mns.NewMNSTopic(topicName, *mnsClient)
		return do(subscriptionManager)
	})
}

func (client *AliyunClient) WithTableStoreClient(instanceName string, do func(*tablestore.TableStoreClient) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the TABLESTORE client if necessary
	tableStoreClient, ok := client.tablestoreconnByInstanceName[instanceName]
	if ok && !client.config.needRefreshCredential() {
		return do(tableStoreClient)
	}
	endpoint := client.config.OtsEndpoint
	if endpoint == "" {
		endpoint = loadEndpoint(client.RegionId, OTSCode)
	}
	if endpoint == "" {
		endpoint = fmt.Sprintf("%s.%s.ots.aliyuncs.com", instanceName, client.RegionId)
	}
	if !strings.HasPrefix(endpoint, "https") && !strings.HasPrefix(endpoint, "http") {
		endpoint = fmt.Sprintf("https://%s", endpoint)
	}
	externalHeaders := make(map[string]string)
	if client.config.SecureTransport == "false" || client.config.SecureTransport == "true" {
		externalHeaders["x-ots-issecuretransport"] = client.config.SecureTransport
	}
	if client.config.SourceIp != "" {
		externalHeaders["x-ots-sourceip"] = client.config.SourceIp
	}
	accessKey, secretKey, token := client.config.GetRefreshCredential()
	tableStoreClient = tablestore.NewClientWithExternalHeader(endpoint, instanceName, accessKey, secretKey, token, tablestore.NewDefaultTableStoreConfig(), externalHeaders)
	client.tablestoreconnByInstanceName[instanceName] = tableStoreClient

	return do(tableStoreClient)
}

func (client *AliyunClient) WithTableStoreTunnelClient(instanceName string, do func(otsTunnel.TunnelClient) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the TABLESTORE tunnel client if necessary
	tunnelClient, ok := client.otsTunnelConnByInstanceName[instanceName]
	if ok && !client.config.needRefreshCredential() {
		return do(tunnelClient)
	}
	endpoint := client.config.OtsEndpoint
	if endpoint == "" {
		endpoint = loadEndpoint(client.RegionId, OTSCode)
	}
	if endpoint == "" {
		endpoint = fmt.Sprintf("%s.%s.ots.aliyuncs.com", instanceName, client.RegionId)
	}
	if !strings.HasPrefix(endpoint, "https") && !strings.HasPrefix(endpoint, "http") {
		endpoint = fmt.Sprintf("https://%s", endpoint)
	}

	externalHeaders := make(map[string]string)
	if client.config.SecureTransport == "false" || client.config.SecureTransport == "true" {
		externalHeaders["x-ots-issecuretransport"] = client.config.SecureTransport
	}
	if client.config.SourceIp != "" {
		externalHeaders["x-ots-sourceip"] = client.config.SourceIp
	}
	accessKey, secretKey, token := client.config.GetRefreshCredential()
	tunnelClient = otsTunnel.NewTunnelClientWithConfigAndExternalHeader(endpoint, instanceName, accessKey, secretKey, token, otsTunnel.DefaultTunnelConfig, externalHeaders)
	client.otsTunnelConnByInstanceName[instanceName] = tunnelClient

	return do(tunnelClient)
}

func (client *AliyunClient) WithCsProjectClient(clusterId, endpoint string, clusterCerts cs.ClusterCerts, do func(*cs.ProjectClient) (interface{}, error)) (interface{}, error) {
	goSdkMutex.Lock()
	defer goSdkMutex.Unlock()

	// Initialize the PROJECT client if necessary
	key := fmt.Sprintf("%s|%s|%s|%s|%s", clusterId, endpoint, clusterCerts.CA, clusterCerts.Cert, clusterCerts.Key)
	csProjectClient, ok := client.csprojectconnByKey[key]
	if !ok {
		var err error
		csProjectClient, err = cs.NewProjectClient(clusterId, endpoint, clusterCerts)
		if err != nil {
			return nil, fmt.Errorf("Getting Application Client failed by cluster id %s: %#v.", clusterCerts, err)
		}
		csProjectClient.SetDebug(false)
		csProjectClient.SetUserAgent(client.config.getUserAgent())
		client.csprojectconnByKey[key] = csProjectClient
	}

	return do(csProjectClient)
}

func (client *AliyunClient) NewCommonRequest(product, serviceCode, schema string, apiVersion ApiVersion) (*requests.CommonRequest, error) {
	endpoint := ""
	product = strings.ToLower(product)
	if _, exist := client.config.Endpoints.Load(product); !exist {
		if err := client.loadEndpoint(product); err != nil {
			return nil, err
		}
	}
	if v, exist := client.config.Endpoints.Load(product); exist && v.(string) != "" {
		endpoint = v.(string)
	}
	request := requests.NewCommonRequest()
	// Use product code to find product domain
	if endpoint != "" {
		request.Domain = endpoint
	} else {
		// When getting endpoint failed by location, using custom endpoint instead
		request.Domain = fmt.Sprintf("%s.%s.aliyuncs.com", strings.ToLower(serviceCode), client.RegionId)
	}
	request.Version = string(apiVersion)
	request.RegionId = client.RegionId
	request.Product = product
	request.Scheme = schema
	request.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	request.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	request.AppendUserAgent(Terraform, client.config.TerraformVersion)
	request.AppendUserAgent(Provider, providerVersion)
	request.AppendUserAgent(Module, client.config.ConfigurationSource)
	request.AppendUserAgent(TerraformTraceId, client.config.TerraformTraceId)
	return request, nil
}

func (client *AliyunClient) AccountId() (string, error) {
	if client.accountId != "" {
		return client.accountId, nil
	}

	client.accountIdMutex.Lock()
	defer client.accountIdMutex.Unlock()

	log.Printf("[DEBUG] account_id not provided, attempting to retrieve it automatically...")
	identity, err := client.GetCallerIdentity()
	if err != nil {
		return "", err
	}
	if identity.AccountId == "" {
		return "", fmt.Errorf("caller identity doesn't contain any AccountId")
	}
	client.accountId = identity.AccountId
	return client.accountId, nil
}

// getAccountType determines and returns the account type (Domestic or International) based on the client's configuration and API endpoint.
// This function first checks if the AccountType is already set in the client configuration. If so, it returns that value directly.
// Otherwise, it defaults the account type to "Domestic" and initializes a request to query available instances through the BssOpenApi API.
// It then determines whether the account is domestic or international based on the API specific errors.
// If there is a specific error, the account type should be updates, and meantime corrects the BssOpenApi endpoint.
func (client *AliyunClient) getAccountType() string {
	if client.config.AccountType != "" {
		return client.config.AccountType
	}
	// Default to Domestic
	accountType := "Domestic"
	productCode := strings.ToLower("BssOpenApi")
	request := map[string]interface{}{
		"PageSize":         "50",
		"PageNum":          1,
		"ProductCode":      "vipcloudfw",
		"ProductType":      "vipcloudfw",
		"SubscriptionType": "Subscription",
	}
	endpoint, err := client.loadApiEndpoint(productCode)
	if err != nil {
		log.Printf("[WARN] getting BssOpenApi endpoint failed. Error: %v", err)
	} else if endpoint == BssOpenAPIEndpointInternational {
		request["ProductCode"] = "cfw"
		request["ProductType"] = "cfw_pre_intl"
		accountType = "International"
	}
	wait := incrementalWait(1*time.Second, 0*time.Second)
	resource.Retry(30*time.Second, func() *resource.RetryError {
		_, err := client.RpcPost("BssOpenApi", "2017-12-14", "QueryAvailableInstances", nil, request, true)
		log.Printf("[WARN] checking caller identity's account type by invoking BssOpenApi QueryAvailableInstances failed. Error: %v", err)
		if err != nil {
			if needRetry(err) {
				wait()
				return resource.RetryableError(err)
			}
			if isExpectedErrors(err, []string{"NotApplicable", "not found article by given param"}) {
				if request["ProductType"] == "vipcloudfw" {
					accountType = "International"
					client.config.Endpoints.Store(productCode, BssOpenAPIEndpointInternational)
				} else {
					accountType = "Domestic"
					client.config.Endpoints.Store(productCode, BssOpenAPIEndpointDomestic)
				}
			} else {
				accountType = ""
			}
		}
		return nil
	})
	return accountType
}
func (client *AliyunClient) IsInternationalAccount() bool {
	return client.config.AccountType == "International"
}
func (client *AliyunClient) isInternationalRegion() bool {
	if client.config.RegionId == "cn-hongkong" {
		return true
	}
	return !strings.HasPrefix(client.config.RegionId, "cn-")
}

func (client *AliyunClient) getSdkConfig(timeout time.Duration) *sdk.Config {
	if timeout == 0 {
		timeout = time.Duration(30) * time.Second
	}
	// WithUserAgent will add a prefix Extra/ for user agent value
	return sdk.NewConfig().
		WithMaxRetryTime(DefaultClientRetryCountSmall).
		WithTimeout(timeout).
		WithEnableAsync(false).
		WithGoRoutinePoolSize(100).
		WithMaxTaskQueueSize(10000).
		WithDebug(false).
		WithHttpTransport(client.getTransport()).
		WithScheme(client.config.Protocol).
		WithUserAgent(fmt.Sprintf("Terraform %s", client.config.getUserAgent()))
}

func (client *AliyunClient) getUserAgent() string {
	return fmt.Sprintf("%s/%s %s/%s %s/%s", Terraform, client.config.TerraformVersion, Provider, providerVersion, Module, client.config.ConfigurationSource)
}

func (client *AliyunClient) getTransport() *http.Transport {
	handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout"))
	if err != nil {
		handshakeTimeout = 120
	}
	transport := &http.Transport{}
	transport.TLSHandshakeTimeout = time.Duration(handshakeTimeout) * time.Second

	return transport
}

func (client *AliyunClient) getHttpProxy() (proxy *url.URL, err error) {
	if client.config.Protocol == "HTTPS" {
		if rawurl := os.Getenv("HTTPS_PROXY"); rawurl != "" {
			proxy, err = url.Parse(rawurl)
		} else if rawurl := os.Getenv("https_proxy"); rawurl != "" {
			proxy, err = url.Parse(rawurl)
		}
	} else {
		if rawurl := os.Getenv("HTTP_PROXY"); rawurl != "" {
			proxy, err = url.Parse(rawurl)
		} else if rawurl := os.Getenv("http_proxy"); rawurl != "" {
			proxy, err = url.Parse(rawurl)
		}
	}
	return proxy, err
}

func (client *AliyunClient) skipProxy(endpoint string) (bool, error) {
	var urls []string
	if rawurl := os.Getenv("NO_PROXY"); rawurl != "" {
		urls = strings.Split(rawurl, ",")
	} else if rawurl := os.Getenv("no_proxy"); rawurl != "" {
		urls = strings.Split(rawurl, ",")
	}
	for _, value := range urls {
		if strings.HasPrefix(value, "*") {
			value = fmt.Sprintf(".%s", value)
		}
		noProxyReg, err := regexp.Compile(value)
		if err != nil {
			return false, err
		}
		if noProxyReg.MatchString(endpoint) {
			return true, nil
		}
	}
	return false, nil
}

func (client *AliyunClient) GetCallerIdentity() (*sts.GetCallerIdentityResponse, error) {
	args := sts.CreateGetCallerIdentityRequest()

	endpoint := client.config.StsEndpoint
	if endpoint == "" {
		endpoint = loadEndpoint(client.config.RegionId, STSCode)
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(STSCode), endpoint)
	}
	stsClient, err := sts.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the STS client: %#v", err)
	}

	stsClient.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	stsClient.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	stsClient.SourceIp = client.config.SourceIp
	stsClient.SecureTransport = client.config.SecureTransport

	identity, err := stsClient.GetCallerIdentity(args)
	if err != nil {
		return nil, err
	}
	if identity == nil {
		return nil, fmt.Errorf("caller identity not found")
	}
	return identity, err
}
func (client *AliyunClient) WithDdosbgpClient(do func(*ddosbgp.Client) (interface{}, error)) (interface{}, error) {
	if client.ddosbgpconn != nil && !client.config.needRefreshCredential() {
		return do(client.ddosbgpconn)
	}
	product := "ddosbgp"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	ddosbgpconn, err := ddosbgp.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the DDOSBGP client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				ddosbgpconn.SetHttpsProxy(proxy.String())
			} else {
				ddosbgpconn.SetHttpProxy(proxy.String())
			}
		}
	}

	ddosbgpconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	ddosbgpconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	ddosbgpconn.SourceIp = client.config.SourceIp
	ddosbgpconn.SecureTransport = client.config.SecureTransport
	client.ddosbgpconn = ddosbgpconn

	return do(client.ddosbgpconn)
}
func (client *AliyunClient) WithAlikafkaClient(do func(*alikafka.Client) (interface{}, error)) (interface{}, error) {
	if client.alikafkaconn != nil && !client.config.needRefreshCredential() {
		return do(client.alikafkaconn)
	}
	product := "alikafka"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	alikafkaconn, err := alikafka.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the ALIKAFKA client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				alikafkaconn.SetHttpsProxy(proxy.String())
			} else {
				alikafkaconn.SetHttpProxy(proxy.String())
			}
		}
	}

	alikafkaconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	alikafkaconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	alikafkaconn.SourceIp = client.config.SourceIp
	alikafkaconn.SecureTransport = client.config.SecureTransport
	client.alikafkaconn = alikafkaconn

	return do(client.alikafkaconn)
}

func (client *AliyunClient) WithEmrClient(do func(*emr.Client) (interface{}, error)) (interface{}, error) {
	if client.emrconn != nil && !client.config.needRefreshCredential() {
		return do(client.emrconn)
	}
	product := "emr"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	emrConn, err := emr.NewClientWithOptions(client.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the E-MapReduce client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				emrConn.SetHttpsProxy(proxy.String())
			} else {
				emrConn.SetHttpProxy(proxy.String())
			}
		}
	}

	emrConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	emrConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	emrConn.SourceIp = client.config.SourceIp
	emrConn.SecureTransport = client.config.SecureTransport
	client.emrconn = emrConn

	return do(client.emrconn)
}

func (client *AliyunClient) WithSagClient(do func(*smartag.Client) (interface{}, error)) (interface{}, error) {
	if client.sagconn != nil && !client.config.needRefreshCredential() {
		return do(client.sagconn)
	}
	product := "smartag"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	sagconn, err := smartag.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the SAG client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				sagconn.SetHttpsProxy(proxy.String())
			} else {
				sagconn.SetHttpProxy(proxy.String())
			}
		}
	}

	sagconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	sagconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	sagconn.SourceIp = client.config.SourceIp
	sagconn.SecureTransport = client.config.SecureTransport
	client.sagconn = sagconn

	return do(client.sagconn)
}

func (client *AliyunClient) WithDbauditClient(do func(*yundun_dbaudit.Client) (interface{}, error)) (interface{}, error) {
	if client.dbauditconn != nil && !client.config.needRefreshCredential() {
		return do(client.dbauditconn)
	}
	product := "yundun_dbaudit"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	dbauditconn, err := yundun_dbaudit.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the DBAUDIT client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				dbauditconn.SetHttpsProxy(proxy.String())
			} else {
				dbauditconn.SetHttpProxy(proxy.String())
			}
		}
	}

	dbauditconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	dbauditconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	dbauditconn.SourceIp = client.config.SourceIp
	dbauditconn.SecureTransport = client.config.SecureTransport
	client.dbauditconn = dbauditconn

	return do(client.dbauditconn)
}
func (client *AliyunClient) WithMarketClient(do func(*market.Client) (interface{}, error)) (interface{}, error) {
	if client.marketconn != nil && !client.config.needRefreshCredential() {
		return do(client.marketconn)
	}
	product := "market"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	marketconn, err := market.NewClientWithOptions(client.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Market client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				marketconn.SetHttpsProxy(proxy.String())
			} else {
				marketconn.SetHttpProxy(proxy.String())
			}
		}
	}

	marketconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	marketconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	marketconn.SourceIp = client.config.SourceIp
	marketconn.SecureTransport = client.config.SecureTransport
	client.marketconn = marketconn

	return do(client.marketconn)
}

func (client *AliyunClient) WithHbaseClient(do func(*hbase.Client) (interface{}, error)) (interface{}, error) {
	if client.hbaseconn != nil && !client.config.needRefreshCredential() {
		return do(client.hbaseconn)
	}
	product := "hbase"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	hbaseconn, err := hbase.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the hbase client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				hbaseconn.SetHttpsProxy(proxy.String())
			} else {
				hbaseconn.SetHttpProxy(proxy.String())
			}
		}
	}

	hbaseconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	hbaseconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	hbaseconn.SourceIp = client.config.SourceIp
	hbaseconn.SecureTransport = client.config.SecureTransport

	client.hbaseconn = hbaseconn

	return do(client.hbaseconn)
}

func (client *AliyunClient) WithAdbClient(do func(*adb.Client) (interface{}, error)) (interface{}, error) {
	if client.adbconn != nil && !client.config.needRefreshCredential() {
		return do(client.adbconn)
	}
	product := "adb"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	adbconn, err := adb.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the adb client: %#v", err)

	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				adbconn.SetHttpsProxy(proxy.String())
			} else {
				adbconn.SetHttpProxy(proxy.String())
			}
		}
	}

	adbconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	adbconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	adbconn.SourceIp = client.config.SourceIp
	adbconn.SecureTransport = client.config.SecureTransport
	client.adbconn = adbconn

	return do(client.adbconn)
}
func (client *AliyunClient) WithCbnClient(do func(*cbn.Client) (interface{}, error)) (interface{}, error) {
	if client.cbnConn != nil && !client.config.needRefreshCredential() {
		return do(client.cbnConn)
	}
	product := "cbn"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	cbnConn, err := cbn.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Cbnclient: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				cbnConn.SetHttpsProxy(proxy.String())
			} else {
				cbnConn.SetHttpProxy(proxy.String())
			}
		}
	}

	cbnConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	cbnConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	cbnConn.SourceIp = client.config.SourceIp
	cbnConn.SecureTransport = client.config.SecureTransport
	client.cbnConn = cbnConn

	return do(client.cbnConn)
}

func (client *AliyunClient) WithEdasClient(do func(*edas.Client) (interface{}, error)) (interface{}, error) {
	if client.edasconn != nil && !client.config.needRefreshCredential() {
		return do(client.edasconn)
	}
	product := "edas"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	edasconn, err := edas.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(time.Duration(60)*time.Second), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the EDAS client: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				edasconn.SetHttpsProxy(proxy.String())
			} else {
				edasconn.SetHttpProxy(proxy.String())
			}
		}
	}

	edasconn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	edasconn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	edasconn.SourceIp = client.config.SourceIp
	edasconn.SecureTransport = client.config.SecureTransport
	client.edasconn = edasconn

	return do(client.edasconn)
}

func (client *AliyunClient) WithAlidnsClient(do func(*alidns.Client) (interface{}, error)) (interface{}, error) {
	if client.alidnsConn != nil && !client.config.needRefreshCredential() {
		return do(client.alidnsConn)
	}
	product := "alidns"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}

	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}

	alidnsConn, err := alidns.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Alidnsclient: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				alidnsConn.SetHttpsProxy(proxy.String())
			} else {
				alidnsConn.SetHttpProxy(proxy.String())
			}
		}
	}

	alidnsConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	alidnsConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	alidnsConn.SourceIp = client.config.SourceIp
	alidnsConn.SecureTransport = client.config.SecureTransport
	client.alidnsConn = alidnsConn
	return do(client.alidnsConn)
}

func (client *AliyunClient) WithCassandraClient(do func(*cassandra.Client) (interface{}, error)) (interface{}, error) {
	if client.cassandraConn != nil && !client.config.needRefreshCredential() {
		return do(client.cassandraConn)
	}
	product := "cassandra"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, product, endpoint)
	}
	cassandraConn, err := cassandra.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Cassandraclient: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				cassandraConn.SetHttpsProxy(proxy.String())
			} else {
				cassandraConn.SetHttpProxy(proxy.String())
			}
		}
	}

	cassandraConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	cassandraConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	cassandraConn.SourceIp = client.config.SourceIp
	cassandraConn.SecureTransport = client.config.SecureTransport
	client.cassandraConn = cassandraConn
	return do(client.cassandraConn)
}

func (client *AliyunClient) WithEciClient(do func(*eci.Client) (interface{}, error)) (interface{}, error) {
	if client.eciConn != nil && !client.config.needRefreshCredential() {
		return do(client.eciConn)
	}
	product := "eci"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}

	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, string(EciCode), endpoint)
	}

	eciConn, err := eci.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the Eciclient: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				eciConn.SetHttpsProxy(proxy.String())
			} else {
				eciConn.SetHttpProxy(proxy.String())
			}
		}
	}

	eciConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	eciConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	eciConn.SourceIp = client.config.SourceIp
	eciConn.SecureTransport = client.config.SecureTransport
	client.eciConn = eciConn
	return do(client.eciConn)
}
func (client *AliyunClient) WithRKvstoreClient(do func(*r_kvstore.Client) (interface{}, error)) (interface{}, error) {
	if client.r_kvstoreConn != nil && !client.config.needRefreshCredential() {
		return do(client.r_kvstoreConn)
	}
	product := "r_kvstore"
	endpoint, err := client.loadApiEndpoint(product)
	if err != nil {
		return nil, err
	}
	if endpoint != "" {
		endpoints.AddEndpointMapping(client.config.RegionId, "r-kvstore", endpoint)
	}
	r_kvstoreConn, err := r_kvstore.NewClientWithOptions(client.config.RegionId, client.getSdkConfig(0), client.config.getAuthCredential(true))
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the RKvstoreclient: %#v", err)
	}

	proxy, err := client.getHttpProxy()
	if proxy != nil {
		skip, err := client.skipProxy(endpoint)
		if err != nil {
			return nil, err
		}

		if !skip {
			if client.config.Protocol == "HTTPS" {
				r_kvstoreConn.SetHttpsProxy(proxy.String())
			} else {
				r_kvstoreConn.SetHttpProxy(proxy.String())
			}
		}
	}

	r_kvstoreConn.SetReadTimeout(time.Duration(client.config.ClientReadTimeout) * time.Millisecond)
	r_kvstoreConn.SetConnectTimeout(time.Duration(client.config.ClientConnectTimeout) * time.Millisecond)
	r_kvstoreConn.SourceIp = client.config.SourceIp
	r_kvstoreConn.SecureTransport = client.config.SecureTransport
	client.r_kvstoreConn = r_kvstoreConn

	return do(client.r_kvstoreConn)
}

func (client *AliyunClient) NewQuotasClientV2() (*openapi.Client, error) {
	productCode := "quotas"
	endpoint := ""
	if v, ok := client.config.Endpoints.Load(productCode); !ok || v.(string) == "" {
		if err := client.loadEndpoint(productCode); err != nil {
			endpoint = "quotas.aliyuncs.com"
			client.config.Endpoints.Store(productCode, endpoint)
			log.Printf("[ERROR] loading %s endpoint got an error: %#v. Using the central endpoint %s instead.", productCode, err, endpoint)
		}
	}
	if v, ok := client.config.Endpoints.Load(productCode); ok && v.(string) != "" {
		endpoint = v.(string)
	}
	if endpoint == "" {
		return nil, fmt.Errorf("[ERROR] missing the product %s endpoint.", productCode)
	}
	openapiConfig := client.teaRpcOpenapiConfig
	openapiConfig.Endpoint = tea.String(endpoint)
	openapiConfig.Protocol = client.teaRpcOpenapiConfig.Protocol
	result, err := openapi.NewClient(&openapiConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the %s client: %#v", productCode, err)
	}
	return result, nil
}
func (client *AliyunClient) loadApiEndpoint(productCode string) (string, error) {
	configEndpoints := productCodeToConfigEndpoints[productCode]
	if configEndpoints == "" {
		configEndpoints = productCode
	}

	if v, ok := client.config.Endpoints.Load(configEndpoints); !ok || v.(string) == "" {
		if err := client.loadEndpoint(productCode); err != nil {
			return "", fmt.Errorf("[ERROR] loading %s endpoint got an error: %#v.", productCode, err)
		}
	} else {
		return client.formatEndpointWithAccountID(productCode, v.(string))
	}
	if v, ok := client.config.Endpoints.Load(productCode); ok && v.(string) != "" {
		return client.formatEndpointWithAccountID(productCode, v.(string))
	}
	return "", fmt.Errorf("[ERROR] missing the product %s endpoint.", productCode)
}

// RpcPost invoking RPC API request with POST method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - API Name
//	query - API parameters in query
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RpcPost(apiProductCode string, apiVersion string, apiName string, query map[string]interface{}, body map[string]interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.rpcRequest("POST", apiProductCode, apiVersion, apiName, query, body, autoRetry, "")
}

// RpcPostWithEndpoint invoking RPC API request with POST method and specified endpoint
// parameters:
//
//		apiProductCode: API Product code, its value equals to the gateway code of the API
//		apiVersion - API version
//		apiName - API Name
//		query - API parameters in query
//		body - API parameters in body
//		autoRetry - whether to auto retry while the runtime has a 5xx error
//	 endpoint - The domain of invoking api
func (client *AliyunClient) RpcPostWithEndpoint(apiProductCode string, apiVersion string, apiName string, query map[string]interface{}, body map[string]interface{}, autoRetry bool, endpoint string) (map[string]interface{}, error) {
	return client.rpcRequest("POST", apiProductCode, apiVersion, apiName, query, body, autoRetry, endpoint)
}

// RpcGet invoking RPC API request with GET method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - API Name
//	query - API parameters in query
//	body - API parameters in body
func (client *AliyunClient) RpcGet(apiProductCode string, apiVersion string, apiName string, query map[string]interface{}, body map[string]interface{}) (map[string]interface{}, error) {
	return client.rpcRequest("GET", apiProductCode, apiVersion, apiName, query, body, true, "")
}

func (client *AliyunClient) rpcRequest(method string, apiProductCode string, apiVersion string, apiName string, query map[string]interface{}, body map[string]interface{}, autoRetry bool, endpoint string) (map[string]interface{}, error) {
	var err error
	if endpoint == "" {
		apiProductCode = strings.ToLower(ConvertKebabToSnake(apiProductCode))
		endpoint, err = client.loadApiEndpoint(apiProductCode)
		if err != nil {
			return nil, err
		}
	}
	sdkConfig := client.teaSdkConfig
	sdkConfig.SetEndpoint(endpoint)
	credential, err := client.config.Credential.GetCredential()
	if err != nil || credential == nil {
		return nil, fmt.Errorf("get credential failed. Error: %#v", err)
	}
	sdkConfig.SetAccessKeyId(*credential.AccessKeyId)
	sdkConfig.SetAccessKeySecret(*credential.AccessKeySecret)
	sdkConfig.SetSecurityToken(*credential.SecurityToken)
	conn, err := rpc.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the %s api client: %#v", apiProductCode, err)
	}
	runtime := &util.RuntimeOptions{}
	runtime.SetAutoretry(autoRetry)
	response, err := conn.DoRequest(tea.String(apiName), nil, tea.String(method), tea.String(apiVersion), tea.String("AK"), query, body, runtime)
	return response, formatError(response, err)
}

// RoaPost invoking ROA API request with POST method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPost(apiProductCode string, apiVersion string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("POST", apiProductCode, apiVersion, "", pathName, query, headers, body, autoRetry)
}

// RoaPut invoking ROA API request with PUT method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPut(apiProductCode string, apiVersion string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("PUT", apiProductCode, apiVersion, "", pathName, query, headers, body, autoRetry)
}

// RoaGet invoking ROA API request with GET method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
func (client *AliyunClient) RoaGet(apiProductCode string, apiVersion string, pathName string, query map[string]*string, headers map[string]*string, body interface{}) (map[string]interface{}, error) {
	return client.roaRequest("GET", apiProductCode, apiVersion, "", pathName, query, headers, body, true)
}

// RoaDelete invoking ROA API request with DELETE method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaDelete(apiProductCode string, apiVersion string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("DELETE", apiProductCode, apiVersion, "", pathName, query, headers, body, autoRetry)
}

// RoaPatch invoking ROA API request with PATCH method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPatch(apiProductCode string, apiVersion string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("PATCH", apiProductCode, apiVersion, "", pathName, query, headers, body, autoRetry)
}

// RoaPostWithApiName invoking ROA API request with POST method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - Request path name
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPostWithApiName(apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("POST", apiProductCode, apiVersion, apiName, pathName, query, headers, body, autoRetry)
}

// RoaPutWithApiName invoking ROA API request with PUT method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - Request path name
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPutWithApiName(apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("PUT", apiProductCode, apiVersion, apiName, pathName, query, headers, body, autoRetry)
}

// RoaGetWithApiName invoking ROA API request with GET method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - Request path name
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
func (client *AliyunClient) RoaGetWithApiName(apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}) (map[string]interface{}, error) {
	return client.roaRequest("GET", apiProductCode, apiVersion, apiName, pathName, query, headers, body, true)
}

// RoaDeleteWithApiName invoking ROA API request with DELETE method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - Request path name
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaDeleteWithApiName(apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("DELETE", apiProductCode, apiVersion, apiName, pathName, query, headers, body, autoRetry)
}

// RoaPatchWithApiName invoking ROA API request with PATCH method
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiVersion - API version
//	apiName - Request path name
//	pathName - Request path name
//	query - API parameters in query
//	headers - API parameters in headers
//	body - API parameters in body
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) RoaPatchWithApiName(apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	return client.roaRequest("PATCH", apiProductCode, apiVersion, apiName, pathName, query, headers, body, autoRetry)
}

func (client *AliyunClient) roaRequest(method string, apiProductCode string, apiVersion string, apiName string, pathName string, query map[string]*string, headers map[string]*string, body interface{}, autoRetry bool) (map[string]interface{}, error) {
	apiProductCode = strings.ToLower(ConvertKebabToSnake(apiProductCode))
	endpoint, err := client.loadApiEndpoint(apiProductCode)
	if err != nil {
		return nil, err
	}
	sdkConfig := client.teaRoaSdkConfig
	sdkConfig.SetEndpoint(endpoint)
	credential, err := client.config.Credential.GetCredential()
	if err != nil || credential == nil {
		return nil, fmt.Errorf("get credential failed. Error: %#v", err)
	}
	sdkConfig.SetAccessKeyId(*credential.AccessKeyId)
	sdkConfig.SetAccessKeySecret(*credential.AccessKeySecret)
	sdkConfig.SetSecurityToken(*credential.SecurityToken)
	conn, err := roa.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the %s api client: %#v", apiProductCode, err)
	}
	var response map[string]interface{}
	runtime := &util.RuntimeOptions{}
	runtime.SetAutoretry(autoRetry)
	if apiName != "" {
		response, err = conn.DoRequestWithAction(tea.String(apiName), tea.String(apiVersion), nil, tea.String(method), tea.String("AK"), tea.String(pathName), query, headers, body, runtime)
	} else {
		response, err = conn.DoRequest(tea.String(apiVersion), nil, tea.String(method), tea.String("AK"), tea.String(pathName), query, headers, body, runtime)
	}
	if respBody, isExist := response["body"]; isExist && respBody != nil {
		response = respBody.(map[string]interface{})
	}
	return response, formatError(response, err)
}

// Do invoking API request with SDK v2
// parameters:
//
//	apiProductCode: API Product code, its value equals to the gateway code of the API
//	apiParams - API params
//	query - API parameters in query
//	body - API parameters in body
//	headers - API parameters in headers
//	hostMap - API parameters in hostMap
//	autoRetry - whether to auto retry while the runtime has a 5xx error
func (client *AliyunClient) Do(apiProductCode string, apiParams *openapi.Params, query map[string]*string, body interface{}, headers map[string]*string, hostMap map[string]*string, autoRetry bool) (map[string]interface{}, error) {
	apiProductCode = strings.ToLower(ConvertKebabToSnake(apiProductCode))
	endpoint, err := client.loadApiEndpoint(apiProductCode)
	if err != nil {
		return nil, err
	}

	sdkConfig := client.teaRoaOpenapiConfig
	if apiParams.Style != nil && *apiParams.Style == "RPC" {
		sdkConfig = client.teaRpcOpenapiConfig
	}
	if apiProductCode == "sls" {
		sdkConfig = openapi.Config{}
	}
	if apiParams.Protocol == nil || *apiParams.Protocol == "" {
		apiParams.Protocol = tea.String(client.config.Protocol)
	}
	sdkConfig.SetEndpoint(endpoint)
	credential, err := client.config.Credential.GetCredential()
	if err != nil || credential == nil {
		return nil, fmt.Errorf("get credential failed. Error: %#v", err)
	}
	sdkConfig.SetAccessKeyId(*credential.AccessKeyId)
	sdkConfig.SetAccessKeySecret(*credential.AccessKeySecret)
	sdkConfig.SetSecurityToken(*credential.SecurityToken)
	sdkConfig.SetUserAgent(client.config.getUserAgent())
	openapiClient, err := openapi.NewClient(&sdkConfig)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize the %s api client: %#v", apiProductCode, err)
	}
	if apiProductCode == "oss" {
		openapiClient.Spi, err = ossclient.NewClient()
		if err != nil {
			return nil, fmt.Errorf("unable to initialize the %s api client: %#v", apiProductCode, err)
		}
		// SignVersion
		if ossV, ok := client.config.SignVersion.Load("oss"); ok {
			openapiClient.SignatureVersion = tea.String(ossV.(string))
		}
	}
	if apiProductCode == "sls" {
		openapiClient.Spi, err = gatewayclient.NewClient()
		if err != nil {
			return nil, fmt.Errorf("unable to initialize the %s api client: %#v", apiProductCode, err)
		}
		openapiClient.Protocol = tea.String(client.config.Protocol)
	}
	var response map[string]interface{}
	runtime := &utilV2.RuntimeOptions{}
	runtime.SetAutoretry(autoRetry)
	if apiParams.Style != nil && *apiParams.Style == "RPC" {
		response, err = openapiClient.CallApi(apiParams, &openapi.OpenApiRequest{Query: query, Body: body, Headers: headers, HostMap: hostMap}, runtime)
	} else {
		response, err = openapiClient.Execute(apiParams, &openapi.OpenApiRequest{Query: query, Body: body, Headers: headers, HostMap: hostMap}, runtime)
	}
	if respBody, isExist := response["body"]; isExist && respBody != nil {
		if v, ok := respBody.(map[string]interface{}); ok {
			response = v
		}
	}
	return response, formatError(response, err)
}
func formatError(response map[string]interface{}, err error) error {
	if err != nil {
		return err
	}
	code, ok1 := response["Code"]
	if !ok1 {
		code, ok1 = response["code"]
	}
	// There is a design in some product api that the response has code with a map type, like FC
	if ok1 && !(isString(code) || isInteger(code)) {
		return err
	}
	// There is a design in some product api that the request is success but its message is empty and the code is 0 or other string
	// like ENS, eflo, apig and so on.
	if ok1 && (strings.ToLower(fmt.Sprint(code)) == "success" ||
		strings.ToLower(fmt.Sprint(code)) == "ok" ||
		fmt.Sprint(code) == "200" ||
		fmt.Sprint(code) == "0") {
		return err
	}
	success, ok2 := response["Success"]
	if !ok2 {
		success, ok2 = response["success"]
	}
	if ok2 && fmt.Sprint(success) == "true" {
		return err
	}
	message, ok3 := response["Message"]
	if !ok3 {
		message, ok3 = response["message"]
	}
	if ok3 && (message == nil || fmt.Sprint(message) == "") {
		return err
	}
	if ok1 || ok2 {
		statusCode := 200
		if v, ok := response["StatusCode"]; ok {
			statusCode = tea.IntValue(v.(*int))
		}
		return tea.NewSDKError(map[string]interface{}{
			"statusCode": statusCode,
			"code":       tea.ToString(code),
			"message":    tea.ToString(message),
			"data":       response,
		})
	}
	return err
}

func (client *AliyunClient) formatEndpointWithAccountID(productCode string, endpoint string) (string, error) {
	if endpoint == "" {
		return endpoint, nil
	}
	switch productCode {
	case "fc_open", "fc":
		accountId, err := client.AccountId()
		if err != nil {
			return endpoint, err
		}
		if strings.HasPrefix(endpoint, accountId) {
			return endpoint, nil
		}
		return fmt.Sprintf("%s.%s", accountId, endpoint), nil
	}
	return endpoint, nil
}

type ossCredentials struct {
	client *AliyunClient
}

func (defCre *ossCredentials) GetAccessKeyID() string {
	value, _, _ := defCre.client.config.GetRefreshCredential()
	return value
}

func (defCre *ossCredentials) GetAccessKeySecret() string {
	_, value, _ := defCre.client.config.GetRefreshCredential()
	return value
}

func (defCre *ossCredentials) GetSecurityToken() string {
	_, _, value := defCre.client.config.GetRefreshCredential()
	return value
}

type ossCredentialsProvider struct {
	client *AliyunClient
}

func (defBuild *ossCredentialsProvider) GetCredentials() oss.Credentials {
	return &ossCredentials{client: defBuild.client}
}

func (client *AliyunClient) GetRetryTimeout(defaultTimeout time.Duration) time.Duration {

	maxRetryTimeout := client.config.MaxRetryTimeout
	if maxRetryTimeout != 0 {
		return time.Duration(maxRetryTimeout) * time.Second
	}

	return defaultTimeout
}
