package regionalapi

import (
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	docs "github.com/tidbcloud/observability/api/regionalserverswagger"
	"github.com/tidbcloud/observability/pkg/common/constants"
	commonInterceptor "github.com/tidbcloud/observability/pkg/common/interceptor"
	"github.com/tidbcloud/observability/pkg/common/metrics"
)

func route(g *gin.Engine) {
	docs.SwaggerInfo.BasePath = "/api/v1"

	// support swagger
	swagger := g.Group("/swagger")
	{
		swagger.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
	}

	// enable pprof
	pprof.Register(g)

	// api
	apiV1 := g.Group("/api/v1")
	apiV1.Use(commonInterceptor.Log, commonInterceptor.UseRDS, gin.CustomRecovery(commonInterceptor.Recover))
	{
		apiV1.GET("/", ServiceInfo)
	}

	health := apiV1.Group("/health")
	health.GET("/", metrics.HandleRegionalAPIMetrics(constants.EndpointServiceHealth), ServiceHealth)

	cluster := apiV1.Group("/clusters")
	cluster.POST("/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterCreate), CreateCluster)
	// Note: register the api without the trailing slash to satisfy the request routed from AWS API Gateway
	// See https://repost.aws/questions/QUi5vp8_WhSnC10LAkUbqggQ/api-gateway-trailing-slash
	cluster.POST("", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterCreate), CreateCluster)
	cluster.GET("/:id", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterDetail), GetClusterDetail)
	cluster.PUT("/:id", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterUpdate), UpdateCluster)
	cluster.DELETE("/:id", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterDelete), DeleteCluster)
	cluster.PUT("/:id/pause", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterPause), PauseCluster)
	cluster.PUT("/:id/resume", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterResume), ResumeCluster)
	cluster.GET("/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterQuery), QueryClusters)
	cluster.GET("", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterQuery), QueryClusters)
	cluster.PUT("/:id/monitor-spec/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterMonitorSpec), ModifyMonitorSpec)
	cluster.PUT("/:id/monitor-mode/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterMonitorMode), ModifyMonitorMode)
	cluster.POST("/:id/monitor-storage/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterMonitorStorage), ModifyMonitorStorage)
	cluster.POST("/:id/monitor-resource/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterMonitorResourceUpdate), ModifyMonitorResource)
	cluster.PUT("/:id/alert/mute", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertMute), MuteClusterAlert)
	cluster.PUT("/:id/alert/unmute", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertUnmute), UnmuteClusterAlert)
	cluster.POST("/:id/agent-observed", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAgentObserved), ModifyAgentObserved)

	// we won't do serviceAuthn for ops api, ip whitelist is applied for safety.
	ops := apiV1.Group("/ops/clusters")
	ops.PUT("/:id/pause-reconcile", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterPauseReconcile), PauseReconcile)

	ruleTemplate := apiV1.Group("/alertrule-template")
	ruleTemplate.POST("/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplate), ModifyAlertRulesTemplate)
	ruleTemplate.POST("", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplate), ModifyAlertRulesTemplate)
	ruleTemplate.DELETE("/:id", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplateDelete), DeleteAlertRulesTemplate)
	ruleTemplate.GET("/:id", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplateQuery), QueryAlertRuleTemplate)
	logging := ruleTemplate.Group("logging")
	logging.POST("/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplate), ApplyLogAlertRules)
	logging.POST("", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesTemplate), ApplyLogAlertRules)

	externals := apiV1.Group("/external/:bizType")
	externals.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externals.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externals.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/alerts", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlerts)
	externals.PUT("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/monitor-alertrules", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesUpdate), UpdateAlertRules)
	externals.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/monitor-alertrules", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRules)
	//for next gen tidb
	externals.GET("/orgs/:tenantID/clusters/:applicationID/", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externals.GET("/orgs/:tenantID/clusters/:applicationID", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externals.GET("/orgs/:tenantID/clusters/:applicationID/alerts", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlerts)
	externals.PUT("/orgs/:tenantID/clusters/:applicationID/monitor-alertrules", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesUpdate), UpdateAlertRules)
	externals.GET("/orgs/:tenantID/clusters/:applicationID/monitor-alertrules", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRules)
	externals.GET("/orgs/:tenantID/byocVcpuPools/observabilityVcpu", metrics.HandleRegionalAPIMetrics(constants.EndpointBYOCObservabilityVcpu), GetByocObservabilityVcpu)

	alertsInternal := apiV1.Group("/internal/:bizType")
	alertsInternal.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/alerts", metrics.HandleRegionalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlerts)
	alertsInternal.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/monitor-alertrules", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRules)

	authApi := apiV1.Group("/auth")
	authApi.GET("/", metrics.HandleRegionalAPIMetrics(constants.EndpointAuthAndRoute), AuthAndRouteForDataAccessing)

	// openapi endpoints
	routeOAPI(g)

	routeV2(g)
}

func routeV2(g *gin.Engine) {
	apiv2 := g.Group("/api/v2")
	apiv2.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))

	observability := apiv2.Group("/observability")
	{
		clusters := observability.Group("/clusters")
		{
			clusters.POST("/", OpenCreateCluster)
			clusters.POST("", OpenCreateCluster)
			clusters.GET("/", OpenGetClusterList)
			clusters.GET("", OpenGetClusterList)
			clusters.GET("/:cluster_id", OpenGetClusterDetail)
			clusters.DELETE("/:cluster_id", OpenDeleteCluster)
			clusters.POST("/:cluster_id/pause", OpenPauseCluster)
			clusters.POST("/:cluster_id/resume", OpenResumeCluster)

			config := clusters.Group("/:cluster_id/:category/config")
			{
				config.PUT("/", OpenUpdateCategoryConfig)
			}

			status := clusters.Group("/:cluster_id/:category/status")
			{
				status.PUT("/", OpenUpdateCategoryStatus)
			}

			clusterRule := clusters.Group("/:cluster_id/alertrule")
			clusterRule.GET("/", OpenGetClusterAlertRule)
			clusterRule.PUT("/", OpenUpdateClusterAlertRule)
		}

		ruleTemplate := observability.Group("/alertrule/templates")
		{
			ruleTemplate.GET("/", OpenGetAlertRuleTemplates)
			ruleTemplate.GET("/:template_id", OpenGetAlertRuleTemplateDetail)
		}
	}
}

func routeOAPI(g *gin.Engine) {
	apiv1beta1 := g.Group("/:bizType/v1beta1")
	apiv1beta1.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))
	apiv1beta2 := g.Group("/:bizType/v1beta2")
	apiv1beta2.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))

	registerOpenAPIIntegrationTaskRoutes(apiv1beta1, "clusters")
	registerOpenAPIIntegrationTaskRoutes(apiv1beta2, "tidbs")

	// Internal route for vector agent integration tasks
	internalIntegrationTask := apiv1beta1.Group("/internal/clusters/:applicationID/monitoringPipelines")
	internalIntegrationTask.POST("", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskCreate), CreateIntegrationTaskInternal)
	internalIntegrationTask.GET("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskDetail), DetailIntegrationTaskInternal)
	internalIntegrationTask.DELETE("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskDelete), DeleteIntegrationTask)
	internalIntegrationTask.GET("", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskQuery), QueryIntegrationTasksInternal)
}

func registerOpenAPIIntegrationTaskRoutes(group *gin.RouterGroup, resourceName string) {
	integrationTask := group.Group("/" + resourceName + "/:applicationID/monitoringPipelines")
	integrationTask.POST("", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskCreate), CreateIntegrationTask)
	integrationTask.GET("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskDetail), DetailIntegrationTask)
	integrationTask.DELETE("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskDelete), DeleteIntegrationTask)
	integrationTask.GET("", metrics.HandleGlobalAPIMetrics(constants.EndpointIntegrationTaskQuery), QueryIntegrationTasks)
}
