package api

import (
	"dataservices/commons/config"
	"dataservices/commons/grpc"
	"dataservices/docs"
	controller "dataservices/internal/controller/central"

	"github.com/gin-gonic/gin"
	swaggerfiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"github.com/tidbcloud/tiinf/transport/http/tigin"
)

func installAuthAndRBACPlugin(router *gin.Engine) {
	// Auth CheckUser
	accountClient := grpc.GetAccountProviderSvcClient()
	var pathPattern2ResourceMap = map[string]string{
		"get:/api/v1/dataservices/central/orgs/(\\d+)/projects/(\\d+)/.*":    "orgs/{org_id}/projects/{project_id}/dataservices/central/get_api",
		"post:/api/v1/dataservices/central/orgs/(\\d+)/projects/(\\d+)/.*":   "orgs/{org_id}/projects/{project_id}/dataservices/central/post_api",
		"put:/api/v1/dataservices/central/orgs/(\\d+)/projects/(\\d+)/.*":    "orgs/{org_id}/projects/{project_id}/dataservices/central/put_api",
		"patch:/api/v1/dataservices/central/orgs/(\\d+)/projects/(\\d+)/.*":  "orgs/{org_id}/projects/{project_id}/dataservices/central/patch_api",
		"delete:/api/v1/dataservices/central/orgs/(\\d+)/projects/(\\d+)/.*": "orgs/{org_id}/projects/{project_id}/dataservices/central/delete_api",
	}
	// uri white list
	var pathWhitelist = []string{"/healthz", "/metrics", "/inner/.*"}
	if !config.IsLocalMode() {
		router.Use(
			CheckUser,
			tigin.CheckRBACPermission(accountClient, pathPattern2ResourceMap, pathWhitelist),
			CheckUserAppPermission,
		)
	} else {
		router.Use(CheckUser)
	}
}

func RouteCentralAPI(router *gin.Engine) {
	router.Any(
		"/healthz", func(c *gin.Context) {
			c.Status(200)
		},
	)

	innerRouter(router.Group("/inner"))

	// Swagger
	docs.SwaggerInfo.BasePath = "/api/v1/dataservices/central"
	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))

	// Error
	router.Use(APIErrorJSONReporter())

	ghappRouter(router.Group("api/v1/dataservices/ghapp"))

	externalRouter(router.Group("/api/v1/dataservices/external"))

	// Cors
	router.Use(Cors())

	installAuthAndRBACPlugin(router)
	// Metric
	router.Use(Metric())
	// Request Body Check
	router.Use(CheckRequestBodySize)

	central := router.Group("/api/v1/dataservices/central")
	{
		central.POST("/orgs/:org_id/projects/:project_id/apps", controller.CreateDataApp)
		central.DELETE("/orgs/:org_id/projects/:project_id/apps/:app_id", controller.DeleteDataApp)
		central.GET("/orgs/:org_id/projects/:project_id/apps", controller.ListDataApps)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id", controller.GetDataApp)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id", controller.UpdateDataApp)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/exportlink", controller.GetDataappExportUrl)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/postmanstatus", controller.CheckPostmanStatus)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/samplestatus", controller.CheckSampleStatus)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/batchexample", controller.BatchGetCodeExample)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/updatestatus", controller.UpdateSampleStatusNormal)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/systemendpoint", controller.GetSystemEndpointConfig)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/systemendpoint", controller.SaveSystemEndpointConfig)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy", controller.DeployDataApp)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/discard", controller.DiscardDataApp)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/config", controller.GetDataAppDeploySetting)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/config", controller.SaveDataAppDeploySetting)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/logs", controller.ListDataAppDeployLogs)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/logs/:log_id", controller.SaveDeploymentLog)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/logs/:log_id/diff", controller.GetDataAppDeploymentLogDiff)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/logs/:log_id/deploy", controller.RedeployDataApp)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/diff", controller.GetDataAppDeployDiff)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/deploy/status", controller.GetDataAppDeploymentStatus)

		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/log", controller.QueryDataAppLog)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/download_log_task", controller.SubmitDownloadDataAPPLogTask)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/download_log_task/:task_id", controller.GetDownloadDataAPPLogTaskStatus)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/log_response_body_switch", controller.ChangeLogResponseBodySwitch)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/misc_statistic", controller.GetDataAppMiscStatistic)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/avg_request_rate", controller.GetDataAppAvgReqRate)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/error_rate", controller.GetDataAppErrorRate)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/avg_response_time", controller.GetDataAppAvgResponseTime)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/response_time_tp99", controller.GetDataAppTP99ResponseTime)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/unauth_req_num", controller.GetDataAppUnauthRequestNum)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/endpoints_rank/most_request", controller.GetDataAppMostReqEndpointRank)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/endpoints_rank/most_error", controller.GetDataAppMostErrorEndpointRank)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/metrics/endpoints_rank/high_latency", controller.GetDataAppHighLatencyEndpointRank)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters", controller.CreateAppCluster)
		central.DELETE("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters/:cluster_id", controller.DeleteAppCluster)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters", controller.ListAppCluster)
		central.GET("/orgs/:org_id/projects/:project_id/clusters/:cluster_id/apps", controller.GetClusterLinkedAPP)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys", controller.CreateApiKey)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys/expire_all", controller.ExpireAllApiKeys)
		central.DELETE("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys/:apikey_id", controller.DeleteApiKey)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys/:apikey_id", controller.UpdateApiKey)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys/:apikey_id/expire_key", controller.ExpireApiKey)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/apikeys", controller.ListApiKeys)

		// chat2query
		central.Any("/orgs/:org_id/projects/:project_id/apps/:app_id/chat2query/*path", controller.Chat2Query)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints", controller.CreateEndpoint)
		central.DELETE("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id", controller.DeleteEndpoint)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id", controller.UpdateEndpoint)
		central.PATCH("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id", controller.PatchEndpoint)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id", controller.GetEndpoint)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints", controller.ListEndpoints)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/dump", controller.DumpEndpoint)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/discard", controller.DiscardEndpoint)
		// auto generate endpoints
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/auto/endpoints", controller.CreateAutoGenerateEndpoints)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/draft/diff", controller.GetEndpointDraftDiff)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/online", controller.OnlineEndpoint)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/offline", controller.OfflineEndpoint)
		// central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/online/records", controller.ListOnlineEndpoint)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/versions", controller.ListEndpointPublishVersions)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/version/:version/status", controller.CheckEndpointDeployStatus)

		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/test", controller.PublishTest)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/example", controller.CodeExample)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/baseurl", controller.EndpointBaseUrl)
		// central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/params", controller.EndpointParams)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/sessions", controller.CreateSession)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/statements", controller.Statements)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters/:cluster_id/dbs", controller.ShowDBs)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters/:cluster_id/dbs/:db_name/tables", controller.ShowTables)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/clusters/:cluster_id/dbs/:db_name/tables/:table_name/vector/fields", controller.ShowVectorFields)

		central.POST("/orgs/:org_id/projects/:project_id/preflight-request", controller.GeneratePreflightRequest)
		central.GET("/orgs/:org_id/projects/:project_id/preflight-request/:request_id/repositories", controller.ListPreflightRequestAuthorizedRepositories)
		central.GET("/orgs/:org_id/projects/:project_id/preflight-request/:request_id/repositories/:repo_id/branches", controller.ListPreflightRequestRepositoryBranches)
		central.POST("/orgs/:org_id/projects/:project_id/preflight-request/:request_id/repositories/:repo_id/validate", controller.PreflightValidateRepoBranchDirectory)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/authorize-url", controller.GetDataappAuthorizeUrl)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/repositories", controller.ListDataappAuthorizedRepositories)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/repositories/:repo_id/branches", controller.ListDataappRepositoryBranches)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/repositories/:repo_id/validate", controller.DataappValidateRepoBranchDirectory)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/github", controller.GetDataappGithubLinkageState)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/github/link", controller.LinkDataappWithGithub)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/github/unlink", controller.UnlinkDataappWithGithub)

		central.GET("/orgs/:org_id/projects/:project_id/list_dataapp_usage", controller.ListDataAppUsage)

		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/oas", controller.GetOpenApiSpecification)
		central.GET("/orgs/:org_id/projects/:project_id/external-apps/:external_app_id/oas", controller.GetOpenApiSpecificationByExternalAppId)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/oas/download", controller.DownloadOpenApiSpecification)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/oas/authorize", controller.AuthorizeOpenApiSpecification)

		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/default-domains", controller.ListDefaultDomains)
		central.POST("/orgs/:org_id/projects/:project_id/apps/:app_id/custom-domains", controller.CreateCustomDomain)
		central.GET("/orgs/:org_id/projects/:project_id/apps/:app_id/custom-domains", controller.ListCustomDomains)
		central.DELETE("/orgs/:org_id/projects/:project_id/apps/:app_id/custom-domains/:custom_domain_id", controller.DeleteCustomDomain)
		central.PUT("/orgs/:org_id/projects/:project_id/apps/:app_id/custom-domains/:custom_domain_id", controller.EditCustomDomain)
	}
}

func innerRouter(router *gin.RouterGroup) {
	router.POST("/create_ddb_table", controller.CreateDDBTable)
	router.POST("/sync_data_to_ddb", controller.SyncDataToDDB)
	router.POST("/fix_sync_cluster", controller.SyncCluster)
	router.POST("/refresh_dataapp", controller.RefreshDataAPP)
	router.POST("/refresh_chat2query", controller.RefreshChat2QueryApp)
	router.GET("/dataservice/projects/:project_id/clusters/:cluster_id/account/playground", controller.GetPlaygroundAccount)
	router.POST("/dataservice/event_record", controller.HandleEventRecord)
	router.POST("/dataservice/download_log_res", controller.ReportDownloadDataAPPLogTaskResult)
	router.POST("/dataservice/custom-domains/notify", controller.InnerNotifyCustomDomainExpired)
}

func ghappRouter(router *gin.RouterGroup) {
	router.POST("/webhook", controller.Webhook)
	router.GET("/callback", controller.Callback)
}

func externalRouter(router *gin.RouterGroup) {
	appExport := router.Group("/appexport", checkDataappJwt)
	appExport.GET("/postman", controller.ExportDataappToPostmanCollection)
	appExport.GET("/openapi", controller.ExportDataappToOpenAPI)
}
