package router

import (
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/infra-provider/pkg/consts"
	docs "github.com/tidbcloud/infra-provider/regional-server/api/swagger"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/clustercli"
	rsconfig "github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/proxy"
	accounthandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/account"
	appboxhandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/appbox"
	clusterhandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/cluster"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/router/interceptor"
	internethandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/internet"
	k8sresourcehandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/k8sresource"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/router/k8supgrade"
	migrationxpawshandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/migration/crossplane/aws"
	migrationxpgcphandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/migration/crossplane/gcp"
	migrationrshandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/migration/dedicatedregional"
	nethandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/network"
	plshandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/privatelinkservice"
	plsMigrationHandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/privatelinkservicemigration"
	vpchandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/vpc"
	vpcpeeringhandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/vpcpeering"
)

type MountFn func(rg *gin.RouterGroup)

const BasePath = "/api/dedicated/v1"

// all path should not having trailing slash
func MountVPCPeeringHandler(handler *vpcpeeringhandler.VPCPeeringHandler) MountFn {
	return func(rg *gin.RouterGroup) {
		vpcpeering := rg.Group("/vpcPeerings")
		vpcpeering.GET("", handler.ListVPCPeering)
		vpcpeering.GET("/:vpcPeeringId", handler.GetVPCPeering)
		vpcpeering.POST("", handler.CreateVPCPeering)
		vpcpeering.POST("/accept", handler.AcceptVPCPeering)
		vpcpeering.DELETE("/:vpcPeeringId", handler.DeleteVPCPeering)
	}
}

func MountRejectVpcEndpointConnectionsHandler(handler *plshandler.PrivateLinkServiceHandler) MountFn {
	return func(rg *gin.RouterGroup) {
		cluster := rg.Group("/clusters")
		cluster.POST("/:clusterId/privateLinkServices/:privateLinkServiceId/rejectVpcEndpointConnections", handler.RejectVpcEndpointConnections)
	}
}

// MountPrivateLinkIPAddressTypeHandler mounts the EKS-only TiDB Group address
// type API locally. Regular TiDB Group CRUD remains a Cluster Server proxy.
func MountPrivateLinkIPAddressTypeHandler(handler *plshandler.PrivateLinkServiceHandler) MountFn {
	return func(rg *gin.RouterGroup) {
		cluster := rg.Group("/clusters")
		path := "/:clusterId/tidbgroups/:tidbGroupId/privateLinkIpAddressType"
		cluster.GET(path, handler.GetPrivateLinkIPAddressType)
		cluster.PUT(path, handler.SetPrivateLinkIPAddressType)
		cluster.POST("/:clusterId/tidbgroups/:tidbGroupId/validatePrivateLinkIpv4Only", handler.ValidatePrivateLinkIPv4Only)
	}
}

// PrivateLinkServiceMounts returns the provider-specific local PrivateLink
// routes. Address-type transitions are supported only by the EKS provider.
func PrivateLinkServiceMounts(provider string, handler *plshandler.PrivateLinkServiceHandler) []MountFn {
	mounts := []MountFn{MountRejectVpcEndpointConnectionsHandler(handler)}
	if provider == consts.ProviderTypeEKS {
		mounts = append(mounts, MountPrivateLinkIPAddressTypeHandler(handler))
	}
	return mounts
}

// These apis are exposed as a reverse proxy with following purposes (caller is the migration process)
// `/import-agent` is used to import metadata to o11y-regional-server, it perfoms some modification
// on aws vpcenpoint before proxying request to o11y-regional-server,(please view the implementation of `ImportAgent`)
// the metadata is used by o11y-regional-server to fill fileds on `cluster` CR and `o11yagent` CR
// which live in o11y regional kube-apiserver.
// `/pause-reconcile` proxies the request directly to o11y-regional-server without any extra logic,
// it tells o11y-regional-server to start reconciling on given `cluster` CR.
func MountO11yAgentAWSMigrationHandler(hp *proxy.O11yRegionalServiceAWSHijackProxy) MountFn {
	return func(rg *gin.RouterGroup) {
		group := rg.Group("/migration/o11yregional")
		group.POST("/ops/clusters/:id/import-agent", hp.ImportAgent)
		group.PUT("/ops/clusters/:id/pause-reconcile", hp.PauseReconcile)
	}
}

// These apis are exposed as a reverse proxy with following purposes (caller is the migration process)
// `/import-agent` is used to import metadata to o11y-regional-server,
// the metadata is used by o11y-regional-server to fill fileds on `cluster` CR and `o11yagent` CR.
// which live in o11y regional kube-apiserver.
// It reads gke caData and inject it into requst body before proxying request to o11y-regional-server (please view the implementation of `ImportAgent`).
// `/pause-reconcile` proxies the request directly to o11y-regional-server without any extra logic,
// it tells o11y-regional-server to start reconciling on given `cluster` CR.
func MountO11yAgentGCPMigrationHandler(hp *proxy.O11yRegionalServiceGCPHijackProxy) MountFn {
	return func(rg *gin.RouterGroup) {
		group := rg.Group("/migration/o11yregional")
		group.POST("/ops/clusters/:id/import-agent", hp.ImportAgent)
		group.PUT("/ops/clusters/:id/pause-reconcile", hp.PauseReconcile)
	}
}

func MountCrossplaneAWSMigrationHandler(handler *migrationxpawshandler.Handler) MountFn {
	return func(rg *gin.RouterGroup) {
		group := rg.Group("/migration/crossplane/aws")
		group.POST("/import/cluster", handler.ImportCluster)
		group.POST("/import/vpc", handler.ImportVPC)
		group.POST("/import/providerconfig", handler.ImportProviderConfig)
		group.POST("/import/address", handler.ImportAddress)
		group.POST("/import/internetgateway", handler.ImportInternetGateway)
		group.POST("/import/launchtemplate", handler.ImportLaunchTemplate)
		group.POST("/import/natgateway", handler.ImportNATGateway)
		group.POST("/import/route", handler.ImportRoute)
		group.POST("/import/routetable", handler.ImportRouteTable)
		group.POST("/import/securitygroup", handler.ImportSecurityGroup)
		group.POST("/import/subnet", handler.ImportSubnet)
		group.POST("/import/vpcendpoint", handler.ImportVPCEndpoint)
		group.POST("/import/vpcendpointserviceconfig", handler.ImportVPCEndpointServiceConfiguration)
		group.POST("/import/nodegroup", handler.ImportNodeGroup)
		group.POST("/import/instanceprofile", handler.ImportInstanceProfile)
		group.POST("/import/policy", handler.ImportPolicy)
		group.POST("/import/rolepolicyattachment", handler.ImportRolePolicyAttachment)
		group.POST("/import/role", handler.ImportRole)
		group.POST("/import/resourcerecordset", handler.ImportResourceRecordSet)
		group.POST("/import/vpcpeeringconnection", handler.ImportVPCPeeringConnection)

		group.GET("/check/cluster", handler.CheckCluster)
		group.GET("/check/vpc", handler.CheckVPC)
		group.GET("/check/address", handler.CheckAddress)
		group.GET("/check/internetgateway", handler.CheckInternetGateway)
		group.GET("/check/launchtemplate", handler.CheckLaunchTemplate)
		group.GET("/check/natgateway", handler.CheckNATGateway)
		group.GET("/check/route", handler.CheckRoute)
		group.GET("/check/routetable", handler.CheckRouteTable)
		group.GET("/check/securitygroup", handler.CheckSecurityGroup)
		group.GET("/check/subnet", handler.CheckSubnet)
		group.GET("/check/vpcendpoint", handler.CheckVPCEndpoint)
		group.GET("/check/vpcendpointserviceconfig", handler.CheckVPCEndpointServiceConfiguration)
		group.GET("/check/nodegroup", handler.CheckNodeGroup)
		group.GET("/check/instanceprofile", handler.CheckInstanceProfile)
		group.GET("/check/policy", handler.CheckPolicy)
		group.GET("/check/rolepolicyattachment", handler.CheckRolePolicyAttachment)
		group.GET("/check/role", handler.CheckRole)
		group.GET("/check/resourcerecordset", handler.CheckResourceRecordSet)
		group.GET("/check/vpcpeeringconnection", handler.CheckVPCPeeringConnection)

		group.PATCH("/reconcile/cluster", handler.ReconcileCluster)
		group.PATCH("/reconcile/vpc", handler.ReconcileVPC)
		group.PATCH("/reconcile/address", handler.ReconcileAddress)
		group.PATCH("/reconcile/internetgateway", handler.ReconcileInternetGateway)
		group.PATCH("/reconcile/launchtemplate", handler.ReconcileLaunchTemplate)
		group.PATCH("/reconcile/natgateway", handler.ReconcileNATGateway)
		group.PATCH("/reconcile/route", handler.ReconcileRoute)
		group.PATCH("/reconcile/routetable", handler.ReconcileRouteTable)
		group.PATCH("/reconcile/securitygroup", handler.ReconcileSecurityGroup)
		group.PATCH("/reconcile/subnet", handler.ReconcileSubnet)
		group.PATCH("/reconcile/vpcendpoint", handler.ReconcileVPCEndpoint)
		group.PATCH("/reconcile/vpcendpointserviceconfig", handler.ReconcileVPCEndpointServiceConfiguration)
		group.PATCH("/reconcile/nodegroup", handler.ReconcileNodeGroup)
		group.PATCH("/reconcile/instanceprofile", handler.ReconcileInstanceProfile)
		group.PATCH("/reconcile/policy", handler.ReconcilePolicy)
		group.PATCH("/reconcile/rolepolicyattachment", handler.ReconcileRolePolicyAttachment)
		group.PATCH("/reconcile/role", handler.ReconcileRole)
		group.PATCH("/reconcile/resourcerecordset", handler.ReconcileResourceRecordSet)
		group.PATCH("/reconcile/vpcpeeringconnection", handler.ReconcileVPCPeeringConnection)
	}
}

func MountDedicatedRegionalMigrationHandler(handler *migrationrshandler.Handler) MountFn {
	return func(rg *gin.RouterGroup) {
		group := rg.Group("/migration/dedicatedregional")
		group.POST("/import/appbox", handler.ImportAPPBox)
		group.POST("/import/approute", handler.ImportAPPRoute)
		group.POST("/import/internet", handler.ImportInternet)
		group.POST("/import/privatelinkservice", handler.ImportPrivateLinkService)
		group.POST("/import/vpcpeering", handler.ImportVPCPeering)
		group.POST("/import/vpc", handler.ImportVPC)
		group.POST("/import/o11yroute", handler.ImportO11yRoute)

		group.GET("/check/appbox", handler.CheckAPPBox)
		group.GET("/check/approute", handler.CheckAPPRoute)
		group.GET("/check/internet", handler.CheckInternet)
		group.GET("/check/privatelinkservice", handler.CheckPrivateLinkService)
		group.GET("/check/vpcpeering", handler.CheckVPCPeering)
		group.GET("/check/vpc", handler.CheckVPC)
		group.GET("/check/o11yroute", handler.CheckO11yRoute)

		group.PATCH("/reconcile/appbox", handler.ReconcileAPPBox)
		group.PATCH("/reconcile/approute", handler.ReconcileAPPRoute)
		group.PATCH("/reconcile/internet", handler.ReconcileInternet)
		group.PATCH("/reconcile/privatelinkservice", handler.ReconcilePrivateLinkService)
		group.PATCH("/reconcile/vpcpeering", handler.ReconcileVPCPeering)
		group.PATCH("/reconcile/vpc", handler.ReconcileVPC)
		group.PATCH("/reconcile/o11yroute", handler.ReconcileO11yRoute)

		group.GET("/diff/eksclusterspec", handler.DiffEKSClusterSpec)
		group.GET("/diff/gkeclusterspec", handler.DiffGKEClusterSpec)
	}
}

func MountCrossplaneGCPMigrationHandler(handler *migrationxpgcphandler.Handler) MountFn {
	return func(rg *gin.RouterGroup) {
		group := rg.Group("/migration/crossplane/gcp")
		group.POST("/import/address", handler.ImportAddress)
		group.POST("/import/firewall", handler.ImportFirewall)
		group.POST("/import/network", handler.ImportNetwork)
		group.POST("/import/router", handler.ImportRouter)
		group.POST("/import/subnetwork", handler.ImportSubnetwork)
		group.POST("/import/cluster", handler.ImportCluster)
		group.POST("/import/nodepool", handler.ImportNodePool)
		group.POST("/import/providerconfig", handler.ImportProviderConfig)
		group.POST("/import/serviceaccountkey", handler.ImportServiceAccountKey)
		group.POST("/import/serviceaccountpolicy", handler.ImportServiceAccountPolicy)
		group.POST("/import/serviceaccount", handler.ImportServiceAccount)
		group.POST("/import/bucketpolicymember", handler.ImportBucketPolicyMember)
		group.POST("/import/peering", handler.ImportPeering)
		group.POST("/import/openidconnectprovider", handler.ImportOIDCProivder)
		group.POST("/import/policy", handler.ImportPolicy)
		group.POST("/import/rolepolicyattachment", handler.ImportRolePolicyAttachment)
		group.POST("/import/role", handler.ImportRole)

		group.GET("/check/address", handler.CheckAddress)
		group.GET("/check/firewall", handler.CheckFirewall)
		group.GET("/check/network", handler.CheckNetwork)
		group.GET("/check/router", handler.CheckRouter)
		group.GET("/check/subnetwork", handler.CheckSubnetwork)
		group.GET("/check/cluster", handler.CheckCluster)
		group.GET("/check/nodepool", handler.CheckNodePool)
		group.GET("/check/serviceaccountkey", handler.CheckServiceAccountKey)
		group.GET("/check/serviceaccountpolicy", handler.CheckServiceAccountPolicy)
		group.GET("/check/serviceaccount", handler.CheckServiceAccount)
		group.GET("/check/bucketpolicymember", handler.CheckBucketPolicyMember)
		group.GET("/check/peering", handler.CheckPeering)
		group.GET("/check/openidconnectprovider", handler.CheckOIDCProvider)
		group.GET("/check/policy", handler.CheckPolicy)
		group.GET("/check/rolepolicyattachment", handler.CheckRolePolicyAttachment)
		group.GET("/check/role", handler.CheckRole)

		group.PATCH("/reconcile/address", handler.ReconcileAddress)
		group.PATCH("/reconcile/firewall", handler.ReconcileFirewall)
		group.PATCH("/reconcile/network", handler.ReconcileNetwork)
		group.PATCH("/reconcile/router", handler.ReconcileRouter)
		group.PATCH("/reconcile/subnetwork", handler.ReconcileSubnetwork)
		group.PATCH("/reconcile/cluster", handler.ReconcileCluster)
		group.PATCH("/reconcile/nodepool", handler.ReconcileNodePool)
		group.PATCH("/reconcile/serviceaccountkey", handler.ReconcileServiceAccountKey)
		group.PATCH("/reconcile/serviceaccountpolicy", handler.ReconcileServiceAccountPolicy)
		group.PATCH("/reconcile/serviceaccount", handler.ReconcileServiceAccount)
		group.PATCH("/reconcile/bucketpolicymember", handler.ReconcileBucketPolicyMember)
		group.PATCH("/reconcile/peering", handler.ReconcilePeering)
		group.PATCH("/reconcile/openidconnectprovider", handler.ReconcileOIDCProvider)
		group.PATCH("/reconcile/policy", handler.ReconcilePolicy)
		group.PATCH("/reconcile/rolepolicyattachment", handler.ReconcileRolePolicyAttachment)
		group.PATCH("/reconcile/role", handler.ReconcileRole)
	}
}

func MountClusterServiceHijackProxyHandler(chp *proxy.ClusterServiceHijackProxy) MountFn {
	return func(rg *gin.RouterGroup) {
		cluster := rg.Group("/clusters")
		cluster.GET("", chp.ListClusters)
		cluster.GET("/:clusterId", chp.GetCluster)
		cluster.POST("/:clusterId/scale", chp.ScaleCluster)
		cluster.POST("/:clusterId/pause", chp.PauseCluster)
		cluster.POST("/:clusterId/resume", chp.ResumeCluster)
	}
}

func MountClusterServiceProxyHandler(rp *proxy.ReverseProxyWrapper, clusterAgentProxy *proxy.ClusterAgentProxy) MountFn {
	return func(rg *gin.RouterGroup) {
		proxyH := gin.WrapH(rp)
		rewriteActionH := gin.WrapH(rp.WithActionFormat())

		rg.POST("/clusterValidations/validateSecurityConfiguration", rewriteActionH)
		cluster := rg.Group("/clusters")

		cluster.GET("/:clusterId/getComponentNodeInfos", rewriteActionH)
		cluster.GET("/:clusterId/getProxyConfidentialInfo", rewriteActionH)

		cluster.GET("/:clusterId/getCaCertificate", rewriteActionH)
		cluster.POST("/:clusterId/setClientCaCertificate", rewriteActionH)

		cluster.POST("/:clusterId/setAuditLogConfiguration", rewriteActionH)
		cluster.GET("/:clusterId/getAuditLogConfiguration", rewriteActionH)
		cluster.PUT("/:clusterId/validateAuditLogConfiguration", rewriteActionH)
		cluster.POST("/:clusterId/listAuditLogs", rewriteActionH)
		cluster.POST("/:clusterId/getAuditLogDownloadURL", rewriteActionH)

		cluster.GET("/:clusterId/getVersionAndConfigurations", rewriteActionH)
		cluster.POST("/:clusterId/setVersionAndConfigurations", rewriteActionH)
		cluster.PUT("/:clusterId/validateVersionAndConfigurations", rewriteActionH)

		cluster.GET("/:clusterId/getClusterRole", rewriteActionH)
		cluster.POST("/:clusterId/checkClusterPublicLBInboundRulesStatus", rewriteActionH)

		cluster.GET("/:clusterId/tidbgroups", proxyH)
		cluster.GET("/:clusterId/tidbgroups/:tidbGroupId", proxyH)
		cluster.DELETE("/:clusterId/tidbgroups/:tidbGroupId", proxyH)
		cluster.PATCH("/:clusterId/tidbgroups/:tidbGroupId", proxyH)
		cluster.POST("/:clusterId/tidbgroups", proxyH)

		// Cluster CR intent and API-managed NodePool resources live behind cluster-server.
		cluster.GET("/:clusterId/gkeHeterogeneous/nodePools", proxyH)
		cluster.GET("/:clusterId/gkeHeterogeneous/nodePools/:componentType", proxyH)
		cluster.POST("/:clusterId/gkeHeterogeneous/deploys", proxyH)
		cluster.PATCH("/:clusterId/gkeHeterogeneous/deploys/:componentType", proxyH)
		cluster.GET("/:clusterId/gkeHeterogeneous/deploys", clusterAgentProxy.ProxyGKEHeterogeneousDeploy(rp, true))
		cluster.GET("/:clusterId/gkeHeterogeneous/deploys/:componentType", clusterAgentProxy.ProxyGKEHeterogeneousDeploy(rp, false))
		cluster.DELETE("/:clusterId/gkeHeterogeneous/deploys/:componentType", proxyH)

		cluster.POST("/:clusterId/gkeHeterogeneous/nodePools", proxyH)

		// Dataplane ComputeClass, Gatekeeper Assign, and Policy Control calls must
		// enter through cluster-agent because they operate on the cluster gateway.
		cluster.POST("/:clusterId/gkeHeterogeneous/computeClasses", clusterAgentProxy.Proxy)
		cluster.GET("/:clusterId/gkeHeterogeneous/computeClasses", clusterAgentProxy.Proxy)
		cluster.PATCH("/:clusterId/gkeHeterogeneous/computeClasses/:name", clusterAgentProxy.Proxy)
		cluster.DELETE("/:clusterId/gkeHeterogeneous/computeClasses/:name", clusterAgentProxy.Proxy)

		cluster.POST("/:clusterId/gkeHeterogeneous/gatekeeperAssigns", clusterAgentProxy.Proxy)
		cluster.GET("/:clusterId/gkeHeterogeneous/gatekeeperAssigns/:componentType", clusterAgentProxy.Proxy)
		cluster.PATCH("/:clusterId/gkeHeterogeneous/gatekeeperAssigns/:name", clusterAgentProxy.Proxy)
		cluster.DELETE("/:clusterId/gkeHeterogeneous/gatekeeperAssigns/:name", clusterAgentProxy.Proxy)

		cluster.POST("/:clusterId/gkePolicyControl:enable", clusterAgentProxy.EnableGKEPolicyControl)
		cluster.GET("/:clusterId/gkePolicyControl", clusterAgentProxy.GetGKEPolicyControl)
	}
}

func MountK8sResourceServiceHandler(handler *k8sresourcehandler.Handler) MountFn {
	return func(rg *gin.RouterGroup) {
		rg.POST("/k8sResources/list", handler.ListK8sResources)
	}
}

func MountK8sUpgradeServiceHandler(handler *k8supgrade.Handler) MountFn {
	return func(rg *gin.RouterGroup) {
		rg.GET("/k8sInfos", handler.ListK8sInformation)
		rg.GET("/k8sInfos/:name", handler.GetK8sInformation)
		rg.POST("/upgradeOperations", handler.CreateUpgradeOperation)
		rg.PATCH("/upgradeOperations/:name", handler.UpdateUpgradeOperation)
		rg.PATCH("/upgradeOperations/:name/annotations", handler.PatchUpgradeOperationAnnotations)
		rg.GET("/upgradeOperations/:name", handler.GetUpgradeOperation)
		rg.DELETE("/upgradeOperations/:name", handler.DeleteUpgradeOperation)
	}
}

func MountAPPBoxServiceHandler(handler *appboxhandler.APPBoxHandler) MountFn {
	return func(rg *gin.RouterGroup) {
		// appbox ops API
		appbox := rg.Group("/appboxes")
		appbox.Use(interceptor.ServiceAuthentication)
		appbox.POST("", handler.CreateAPPBox)
		appbox.DELETE("/:name", handler.DeleteAPPBox)
		appbox.GET("/:name", handler.GetAPPBox)
		// normal API
		appbox.GET("", handler.ListAPPBoxes)
	}
}

func MountCompatNeworkServiceHandler(handler *nethandler.NetworkHandler) MountFn {
	return func(rg *gin.RouterGroup) {
		// compat API, should be removed in the future
		compatnetwork := rg.Group("/compatnetworks")
		compatnetwork.Use(interceptor.ServiceAuthentication)
		compatnetwork.GET("/:vpcId", handler.GetNetwork)

		// comapt network ops API
		compatnetwork.DELETE("", handler.DeleteNetwork)

		// compat API, should be removed in the future
		regionconfig := rg.Group("/regionconfig")
		regionconfig.GET("", handler.GetRegionConfig)
	}
}

func route(g *gin.Engine, cli client.Client, clusterCli clustercli.ClusterClient, config rsconfig.RootConfig, tmplconfig rsconfig.TemplatesConfig, mounts ...MountFn) {
	docs.SwaggerInfo.BasePath = BasePath
	// support swagger
	swagger := g.Group("/swagger")
	{
		swagger.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
	}

	// enable pprof
	pprof.Register(g)

	// api
	apiV1 := g.Group(BasePath)
	apiV1.Use(interceptor.HTTPMetricsMiddleware())
	apiV1.Use(interceptor.ServiceAuthentication)
	{
		apiV1.GET("/", ServiceInfo)
		apiV1.GET("/ping", Ping)
	}

	vpc := apiV1.Group("/vpcs")
	vpcHandler := vpchandler.NewVPCHandler(cli, config)
	vpc.POST("", vpcHandler.CreateVPC)
	vpc.GET("", vpcHandler.ListVPC)
	vpc.GET("/queryVpcClusterIds", vpcHandler.GetVPCClusterIds)
	vpc.GET("/:vpcId", vpcHandler.GetVPC)
	// VPC ops API
	vpc.DELETE("/:vpcId", vpcHandler.DeleteVPC)
	vpc.POST("/:vpcId/addSecondaryCidr", vpcHandler.AddSecondaryCidr)

	cluster := apiV1.Group("/clusters")
	clusterHandler := clusterhandler.NewClusterHandler(cli, config, tmplconfig)
	cluster.POST("", clusterHandler.CreateCluster)
	cluster.DELETE("/:clusterId", clusterHandler.DeleteCluster)

	plsHandler := plshandler.NewPrivateLinkServiceHandler(cli, config)
	cluster.GET("/:clusterId/privateLinkServices", plsHandler.ListPrivateLinkServices)
	cluster.GET("/:clusterId/privateLinkServices/:privateLinkServiceId", plsHandler.GetPrivateLinkService)
	cluster.POST("/:clusterId/privateLinkServices", plsHandler.CreatePrivateLinkService)
	cluster.DELETE("/:clusterId/privateLinkServices/:privateLinkServiceId", plsHandler.DeletePrivateLinkService)
	cluster.PATCH("/:clusterId/privateLinkServices/:privateLinkServiceId", plsHandler.UpdatePrivateLinkService)
	cluster.POST("/:clusterId/privateLinkServices/:privateLinkServiceId/pause", plsHandler.PausePrivateLinkService)
	cluster.POST("/:clusterId/privateLinkServices/:privateLinkServiceId/purgeMetadata", plsHandler.PurgePrivateLinkMetadata)

	actHandler := accounthandler.NewAccountHandler(cli, config)
	account := apiV1.Group("/accounts")
	account.POST("/assignAccount", actHandler.PreAssignAccount)
	account.GET("", actHandler.GetAccount)

	// internet ops API
	internet := apiV1.Group("/internets")
	internetHandler := internethandler.NewInternetHandler(cli, config)
	internet.Use(interceptor.ServiceAuthentication)
	internet.POST("", internetHandler.CreateInternet)
	internet.DELETE("/:namespace/:name", internetHandler.GetInternet)
	internet.GET("/:namespace/:name", internetHandler.GetInternet)

	// private link service migration ops API
	plsMigrationHandler := plsMigrationHandler.NewPrivateLinkServiceMigrationHandler(cli, clusterCli, config)
	apiV1.POST("/privateLinkServiceMigrations", plsMigrationHandler.CreatePrivateLinkServiceMigration)
	apiV1.GET("/privateLinkServiceMigrations/:privateLinkServiceMigrationId", plsMigrationHandler.GetPrivateLinkServiceMigration)
	apiV1.GET("/privateLinkServiceMigrations", plsMigrationHandler.ListPrivateLinkServiceMigrations)

	for _, mnt := range mounts {
		mnt(apiV1)
	}
}
