package globalapi

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	docs "github.com/tidbcloud/observability/api/globalserverswagger"
	"github.com/tidbcloud/observability/pkg/common/constants"
	commonInterceptor "github.com/tidbcloud/observability/pkg/common/interceptor"
	"github.com/tidbcloud/observability/pkg/common/metrics"
	"github.com/tidbcloud/observability/pkg/globalapi/interceptor"
	"github.com/tidbcloud/observability/pkg/globalservice"
	"github.com/tidbcloud/tiinf/sdk/auth"
)

func route(g *gin.Engine) {
	// Probes for kubelet — no auth, no request logging. Reachable on the pod port (8080).
	g.GET("/livez", func(c *gin.Context) { c.String(http.StatusOK, "ok") })
	g.GET("/readyz", func(c *gin.Context) {
		m := globalservice.GetGlobalManager()
		if m == nil || m.GetGlobalMetaCache() == nil {
			c.String(http.StatusServiceUnavailable, "global meta cache not ready")
			return
		}
		c.String(http.StatusOK, "ok")
	})

	docs.SwaggerInfo.BasePath = "/api/v1"

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

	// api
	apiV1 := g.Group("/api/v1")
	apiV1.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))

	{
		apiV1.GET("/", ServiceInfo)
	}

	health := apiV1.Group("/health")
	health.Use(interceptor.Auth)
	health.Use(interceptor.ResolveRequestParameters)
	health.GET("/", metrics.HandleGlobalAPIMetrics(constants.EndpointServiceHealth), ServiceHealth)

	cluster := apiV1.Group("/clusters")
	cluster.Use(interceptor.Auth)
	cluster.Use(interceptor.ResolveRequestParameters)

	cluster.POST("/", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterCreate), CreateCluster)
	cluster.GET("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterDetail), DetailCluster)
	cluster.PUT("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterUpdate), UpdateCluster)
	cluster.DELETE("/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterDelete), DeleteCluster)
	cluster.PUT("/:id/pause", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterPause), PauseCluster)
	cluster.PUT("/:id/resume", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterResume), ResumeCluster)
	cluster.GET("/", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterQuery), QueryClusters)
	cluster.PUT("/:id/monitor-spec/", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterMonitorSpec), ModifyMonitorSpec)
	cluster.PUT("/:id/monitor-mode/", metrics.HandleRegionalAPIMetrics(constants.EndpointClusterMonitorMode), ModifyMonitorMode)
	cluster.POST("/:id/monitor-storage/", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterMonitorStorage), ModifyMonitorStorage)
	cluster.POST("/:id/monitor-resource/", metrics.HandleGlobalAPIMetrics(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.HandleGlobalAPIMetrics(constants.EndpointClusterAgentObserved), ModifyAgentObserved)

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

	externals := apiV1.Group("/external/:bizType")
	externals.Use(interceptor.ResolveRequestParameters)

	externalUser := externals.Group("/orgs/:tenantID/projects/:projectID/clusters/:applicationID")
	externalUser.Use(interceptor.UserAuth, interceptor.PermissionCheckerAdapter)
	externalUser.GET("/", metrics.HandleGlobalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externalUser.GET("/alerts", metrics.HandleGlobalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlerts)
	externalUser.PUT("/monitor-alertrules", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterAlertRulesUpdate), UpdateAlertRules)
	externalUser.GET("/monitor-alertrules", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRules)

	// external for next gen tidb
	externalUserNextGen := externals.Group("/orgs/:tenantID/clusters/:applicationID")
	externalUserNextGen.Use(interceptor.UserAuth, interceptor.PermissionCheckerAdapter)
	externalUserNextGen.GET("/", metrics.HandleGlobalAPIMetrics(constants.EndpointExternalGetCluster), GetClusterExternal)
	externalUserNextGen.GET("/alerts", metrics.HandleGlobalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlertsNextGen)
	externalUserNextGen.PUT("/monitor-alertrules", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterAlertRulesUpdate), UpdateAlertRulesNextGen)
	externalUserNextGen.GET("/monitor-alertrules", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRulesNextGen)

	externalByocVcpu := externals.Group("/orgs/:tenantID/byocVcpuPools")
	externalByocVcpu.Use(interceptor.UserAuth, interceptor.PermissionCheckerAdapter)
	externalByocVcpu.GET("/observabilityVcpu", metrics.HandleGlobalAPIMetrics(constants.EndpointBYOCObservabilityVcpu), GetByocObservabilityVcpu)

	// alertsInternal is used to get cluster external alerts for internal services
	alertsInternal := apiV1.Group("/internal/:bizType")
	alertsInternal.Use(interceptor.ResolveRequestParameters)
	alertsInternal.Use(interceptor.Auth)
	alertsInternal.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/alerts", metrics.HandleGlobalAPIMetrics(constants.EndpointExternalAlertQuery), ListClusterExternalAlertsInternal)
	alertsInternal.GET("/orgs/:tenantID/projects/:projectID/clusters/:applicationID/monitor-alertrules", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterAlertRulesQuery), QueryAlertRulesInternal)

	globalClusters := apiV1.Group("/global/clusters")
	globalClusters.Use(interceptor.Auth)
	globalClusters.GET("/", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterQueryGlobally), QueryClustersGlobally)

	ops := apiV1.Group("/ops")
	ops.POST("/events/alerts", metrics.HandleGlobalAPIMetrics(constants.EndpointHandleAlerts), HandleAlerts)

	// routes api for query region route.
	routes := apiV1.Group("/routes")
	routes.Use(interceptor.ResolveRequestParameters)
	routes.Use(interceptor.Auth)
	routes.GET("", metrics.HandleGlobalAPIMetrics(constants.EndpointQueryRoutes), QueryRegionRoutes)
	// internal API only accessible in the private network, should not be exposed via ingress to public
	internalRoutes := g.Group("/internal/v1/routes")
	internalRoutes.Use(interceptor.ResolveRequestParameters)
	internalRoutes.GET("", metrics.HandleGlobalAPIMetrics(constants.EndpointQueryRoutes), QueryRegionRoutes)

	// cluster list with metadata API — registered separately from routeV2
	// to avoid the OpenAPIBizType and ResolveRequestParameters middleware
	clusterListV2 := g.Group("/api/v2/global/clusters")
	clusterListV2.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))
	clusterListV2.Use(interceptor.Auth)
	clusterListV2.GET("", metrics.HandleGlobalAPIMetrics(constants.EndpointClusterListWithMetadata), ListClustersWithMetadata)

	// openapi
	routeOAPI(g)

	// ops-server api
	routeOpsServer(g)

	// ---------------------------- deprecated --------------------------------
	routeV2(g)
}

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

	observability := apiv2.Group("/observability")
	{
		clusters := observability.Group("/clusters")
		{
			clusters.POST("/", OpenCreateCluster)
			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)
		}

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

func routeOAPI(g *gin.Engine) {
	apiv1beta1 := g.Group("/:bizType/v1beta1")
	apiv1beta2 := g.Group("/:bizType/v1beta2")
	apiv1beta1Internal := g.Group("/:bizType/v1beta1/internal")
	auth.InitAccountClientByEndpoint("account-provider-grpc:10000", 0)
	registerOpenAPIIntegrationTaskRoutes(apiv1beta1, "clusters")
	registerOpenAPIIntegrationTaskRoutes(apiv1beta2, "tidbs")

	// Internal route for integration tasks
	internalIntegrationTask := apiv1beta1Internal.Group("/clusters/:applicationID/monitoringPipelines")
	internalIntegrationTask.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))
	internalIntegrationTask.Use(interceptor.InternalAuth)
	internalIntegrationTask.Use(interceptor.ResolveRequestParameters)
	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), QueryIntegrationTasks)
}

func registerOpenAPIIntegrationTaskRoutes(group *gin.RouterGroup, resourceName string) {
	group.Use(interceptor.OAPIBasicInfo)
	// authentication is applied at kong gateway
	group.Use(
		auth.GinGetIdentityInfoInterceptor(&auth.IdentityAuthOption{
			EnableLog: true,
		}),
		auth.GinPermissionCheckInterceptor(&auth.PermissionCheckerCommonParam{
			IdentityF:               auth.GinGetIdentityFunc,
			PathPattern2ResourceMap: interceptor.OpenAPIPathPatternResourceMap,
			GenAccessParam4GinF:     interceptor.GenAccessParamWithPrefixOrgPrj,
		}),
	)
	// group.Use(interceptor.PermissionCheckerAdapter)
	group.Use(interceptor.ResolveRequestParameters)

	integrationTask := group.Group(fmt.Sprintf("/%s/:applicationID/monitoringPipelines", resourceName))
	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)
}

func routeOpsServer(g *gin.Engine) {
	// api
	opsV1 := g.Group("/ops/api/v1")
	opsV1.Use(commonInterceptor.Log, gin.CustomRecovery(commonInterceptor.Recover))
	opsV1.Use(interceptor.Auth)
	opsV1.Use(interceptor.ResolveRequestParameters)

	// VM Cluster CRUD operations (legacy - specific to VM clusters)
	opsV1.POST("/vmclusters", metrics.HandleGlobalAPIMetrics(constants.EndpointVMClusterCreate), CreateVMCluster)
	opsV1.GET("/vmclusters", metrics.HandleGlobalAPIMetrics(constants.EndpointVMClusterList), ListVMClusters)
	opsV1.GET("/vmclusters/:vmclusterId", metrics.HandleGlobalAPIMetrics(constants.EndpointVMClusterGet), GetVMCluster)
	opsV1.PUT("/vmclusters/:vmclusterId", metrics.HandleGlobalAPIMetrics(constants.EndpointVMClusterUpdate), UpdateVMCluster)
	opsV1.DELETE("/vmclusters/:vmclusterId", metrics.HandleGlobalAPIMetrics(constants.EndpointVMClusterDelete), DeleteVMCluster)

	// General HelmRelease CRUD operations
	opsV1.POST("/helmreleases", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseCreate), CreateHelmRelease)
	opsV1.GET("/helmreleases", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseList), ListHelmReleases)
	opsV1.POST("/helmreleases/diff", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseDiff), DiffHelmRelease)
	opsV1.GET("/helmreleases/:name", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseGet), GetHelmRelease)
	opsV1.PUT("/helmreleases/:name", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseUpdate), UpdateHelmRelease)
	opsV1.DELETE("/helmreleases/:name", metrics.HandleGlobalAPIMetrics(constants.EndpointHelmReleaseDelete), DeleteHelmRelease)

	// Upgrade operations
	opsV1.POST("/upgradeOperations", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationCreate), CreateUpgradeOperation)
	opsV1.POST("/upgradeOperations/dry-run", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationDryRun), DryRunUpgradeOperation)
	opsV1.GET("/upgradeOperations", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationList), ListUpgradeOperations)
	opsV1.GET("/upgradeOperations/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationGet), GetUpgradeOperation)
	opsV1.PUT("/upgradeOperations/:id/pause", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationPause), PauseUpgradeOperation)
	opsV1.PUT("/upgradeOperations/:id/resume", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationResume), ResumeUpgradeOperation)
	opsV1.DELETE("/upgradeOperations/:id", metrics.HandleGlobalAPIMetrics(constants.EndpointUpgradeOperationDelete), DeleteUpgradeOperation)

	// Node groups (cluster upgrade related)
	opsV1.GET("/nodeGroups", metrics.HandleGlobalAPIMetrics(constants.EndpointNodeGroupsGet), GetNodeGroups)

	// Addons (cluster upgrade related)
	opsV1.GET("/addons", metrics.HandleGlobalAPIMetrics(constants.EndpointAddonsList), ListAddons)

	// update alertmanager basic auth
	opsV1.PUT("/alert-auth", metrics.HandleGlobalAPIMetrics(constants.EndpointAlertAuthUpdate), UpdateBasicAuth)
}
