// Package server holds HTTP server setup.
package server

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	clinic_internal_api "github.com/pingcap-inc/clinic/clinic_data_service/pkg/clinic-internal-api"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/clustercontext"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/config"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/handlers"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/service"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/ticdc"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/tidbmgmthttp"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.opentelemetry.io/otel/trace"
	"go.uber.org/zap"
)

// NewRouter constructs a Gin engine, registers middlewares and routes.
// Routes:
// - POST /traffic-mirroring/query-comparison: Query Comparison API
// - GET  /healthz: health check endpoint
func NewRouter() *gin.Engine {
	ctx := log.StartTrace("server", "router_init")
	defer log.EndTrace(ctx)

	r := gin.New()

	// 禁用 HTML 转义，避免 & 被转义为 \u0026
	// r.UseRawPath = true
	// r.UnescapePathValues = true

	r.Use(
		gin.LoggerWithFormatter(formatDataServiceAccessLog),
		gin.Recovery(),
	)

	RegisterMiddlewares(r)

	dataProxyCfg, dataProxySvc := initDataProxy(ctx)
	var clusterDiffSvc *service.ClusterDiffApiService
	if dataProxySvc != nil && dataProxyCfg != nil {
		clusterTargetReader := clinic_internal_api.NewClientWithTimeout(dataProxyCfg.ClinicAPIBase, 2*time.Minute)
		clusterDiffSvc = service.NewClusterDiffApiService(dataProxySvc, clusterTargetReader)
	}
	trafficMirroringHandler := handlers.NewTrafficMirroringHandler(clusterDiffSvc)

	// clinic data service
	r.POST("/clinic-internal/traffic-mirroring/query-comparison", trafficMirroringHandler.QueryComparison)
	r.POST("/clinic-internal/traffic-mirroring/report", trafficMirroringHandler.CreateDiffReport)

	// ec2 后续 下线的
	// r.POST("/traffic-mirroring/query-comparison", handlers.QueryComparison)
	// r.POST("/traffic-mirroring/report", handlers.CreateDiffReport)
	// r.POST("/slowquery/o11y-query", handlers.O11yQueryProxy)

	// r.POST("/clinic-internal/healthcheck/sql/slowquery-count", handlers.SqlCheck)
	r.GET("/healthz", func(c *gin.Context) {
		c.JSON(200, gin.H{"status": "ok"})
	})

	// Prometheus scrape (Data Proxy & process metrics)
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	// Versioned resource routes are independent from warehouse/query engines.
	registerClinicResourceRoutes(ctx, r, "configs/config-dataproxy.yaml")

	// DataProxy routes (如果配置文件存在)
	registerDataProxyRoutes(ctx, r, dataProxyCfg, dataProxySvc)

	return r
}

func formatDataServiceAccessLog(param gin.LogFormatterParams) string {
	ctx := context.Background()
	if param.Request != nil {
		ctx = param.Request.Context()
	}
	sc := trace.SpanContextFromContext(ctx)
	tid := ""
	if sc.HasTraceID() {
		tid = sc.TraceID().String()
	}
	path := param.Path
	if param.Request != nil && isBYOCStatementsPath(param.Request.URL.Path) {
		// Statements filters may contain customer SQL in the `text` query
		// parameter. Keep the route observable without persisting its query.
		path = param.Request.URL.Path
	}
	return fmt.Sprintf("[GIN] %s | %3d | %s | %s |%-7s %s | trace_id=%s\n",
		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
		param.StatusCode,
		param.Latency,
		param.ClientIP,
		param.Method,
		path,
		tid,
	)
}

func isBYOCStatementsPath(path string) bool {
	parts := strings.Split(path, "/")
	return len(parts) >= 9 && parts[0] == "" && parts[1] == "data-proxy" && parts[2] == "api" &&
		parts[3] == "v1" && parts[4] == "orgs" && parts[5] != "" && parts[6] == "clusters" &&
		parts[7] != "" && parts[8] == "statements"
}

func registerClinicResourceRoutes(ctx context.Context, r *gin.Engine, configPath string) {
	clinicAPIBase, err := config.LoadClinicAPIBase(configPath)
	if err != nil {
		panic(fmt.Sprintf("load Clinic internal API config: %v", err))
	}
	tidbMgmtHTTPBase, err := config.LoadTidbMgmtHTTPBase(configPath)
	if err != nil {
		panic(fmt.Sprintf("load TiDB management HTTP API config: %v", err))
	}
	client := clinic_internal_api.NewClientWithTimeout(clinicAPIBase, 10*time.Second)
	repository := clustercontext.NewClinicRepository(client)
	userRepository := clustercontext.NewUserScopedRepository(nil, nil)
	if tidbMgmtHTTPBase == "" {
		log.Warn(ctx, "TiDB management HTTP API is not configured; public user-scoped cluster list is unavailable")
	} else {
		visibilityClient, err := tidbmgmthttp.NewClient(tidbMgmtHTTPBase, 10*time.Second)
		if err != nil {
			panic(fmt.Sprintf("create TiDB management HTTP API client: %v", err))
		}
		userRepository = clustercontext.NewUserScopedRepository(client, visibilityClient)
	}
	metadataHandler := handlers.NewClusterMetadataHandlerWithUserRepository(repository, userRepository)
	metadataHandler.RegisterRoutes(r)

	ticdcConfig, err := config.LoadTiCDCConfig(configPath)
	if err != nil {
		panic(fmt.Sprintf("load TiCDC metadata config: %v", err))
	}
	ticdcClients, err := ticdc.NewClients(ticdcConfig.DataflowAddr, ticdcConfig.DataflowNGAddr, ticdcConfig.ServerlessAddr)
	if err != nil {
		panic(fmt.Sprintf("create TiCDC metadata clients: %v", err))
	}
	ticdcHandler := handlers.NewTiCDCHandler(repository, ticdc.NewService(ticdcClients))
	ticdcHandler.RegisterRoutes(r)

	activityHandler := handlers.NewActivityHandler(client)
	activityHandler.RegisterRoutes(r)

	componentsHandler := handlers.NewComponentsHandler(client)
	componentsHandler.RegisterRoutes(r)

	// O11y diagnosis APIs use the shared 120-second query timeout without
	// lengthening the metadata/activity/components client timeout above.
	queryClient := clinic_internal_api.NewClientWithTimeout(clinicAPIBase, 2*time.Minute)
	healthCheckReportHandler := handlers.NewHealthCheckReportHandler(queryClient)
	healthCheckReportHandler.RegisterRoutes(r)

	topRUService := service.NewTopRUService(queryClient)
	topRUHandler := handlers.NewTopRUHandler(topRUService)
	topRUHandler.RegisterRoutes(r)

	// BYOC Slow Query is a direct, read-only O11y diagnosis proxy. Register it
	// independently of the warehouse Data Proxy so it never falls back to the
	// SQL, Athena, S3, or table-management path.
	byocSlowQueryService := service.NewBYOCSlowQueryService(queryClient)
	byocSlowQueryHandler := handlers.NewBYOCSlowQueryHandler(byocSlowQueryService)
	byocSlowQueryHandler.RegisterRoutes(r)

	// BYOC Top SQL resolves the same server-owned diagnosis target and calls
	// only its fixed O11y endpoints. It is independent of warehouse startup and
	// remains behind the canonical /data-proxy/api/v1 gateway authentication.
	byocTopSQLService := service.NewBYOCTopSQLService(queryClient)
	byocTopSQLHandler := handlers.NewBYOCTopSQLHandler(byocTopSQLService)
	byocTopSQLHandler.RegisterRoutes(r)

	// BYOC Continuous Profiling group list/detail reads customer-side data only
	// through the server-resolved O11y diagnosis target. Keep these routes outside
	// warehouse startup so they cannot fall back to SQL, object storage, table
	// management, or any other Tier's diagnosis path.
	byocContinuousProfilingService := service.NewBYOCContinuousProfilingService(queryClient)
	byocContinuousProfilingHandler := handlers.NewBYOCContinuousProfilingHandler(byocContinuousProfilingService)
	byocContinuousProfilingHandler.RegisterRoutes(r)

	// BYOC Statements use the same server-resolved customer-side O11y target,
	// but expose only the fixed live Statements API family. They remain outside
	// warehouse initialization and have no SQL or object-storage fallback.
	byocStatementsService := service.NewBYOCStatementsService(queryClient)
	byocStatementsHandler := handlers.NewBYOCStatementsHandler(byocStatementsService)
	byocStatementsHandler.RegisterRoutes(r)

	log.Info(ctx, "Clinic-backed resource routes registered", zap.String("path", "/data-proxy/api/v1"))
	log.Info(ctx, "Activity route registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/activities"))
	log.Info(ctx, "Components route registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/components"))
	log.Info(ctx, "Health Check report routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/health-check/reports"))
	log.Info(ctx, "Top RU routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/top-ru"))
	log.Info(ctx, "BYOC Slow Query routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/slow-queries"))
	log.Info(ctx, "BYOC Top SQL routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/top-sql"))
	log.Info(ctx, "BYOC Continuous Profiling routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/continuous-profiling"))
	log.Info(ctx, "BYOC Statements routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/statements"))
	log.Info(ctx, "TiCDC metadata routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/ticdc"))
}

func initDataProxy(ctx context.Context) (*config.DataProxyConfig, *service.DataProxyService) {
	dataProxyCfg, err := config.LoadDataProxyConfig("configs/config-dataproxy.yaml")
	if err != nil {
		log.Error(ctx, "DataProxy config not loaded", zap.Error(err))
		return nil, nil
	}

	dataProxySvc, err := service.NewDataProxyService(dataProxyCfg)
	if err != nil {
		log.Error(ctx, "DataProxy service init failed", zap.Error(err))
		return dataProxyCfg, nil
	}
	return dataProxyCfg, dataProxySvc
}

// registerDataProxyRoutes 注册所有依赖 DataProxy 配置的路由
func registerDataProxyRoutes(ctx context.Context, r *gin.Engine, dataProxyCfg *config.DataProxyConfig, dataProxySvc *service.DataProxyService) {
	if dataProxyCfg == nil {
		log.Info(ctx, "DataProxy config not loaded, skipping DataProxy routes")
		return
	}
	canonicalClinicClient := clinic_internal_api.NewClientWithTimeout(dataProxyCfg.ClinicAPIBase, 2*time.Minute)
	canonicalOwnershipRepository := clustercontext.NewClinicRepository(canonicalClinicClient)

	// DataProxy query routes
	if dataProxySvc == nil {
		log.Error(ctx, "DataProxy service unavailable, skipping DataProxy query routes")
	} else {
		dataProxyHandler := handlers.NewDataProxyHandler(dataProxySvc, dataProxyCfg)
		dataProxyHandler.RegisterRoutes(r)

		canonicalDataProxyHandler := handlers.NewCanonicalDataProxyHandler(
			dataProxySvc,
			canonicalOwnershipRepository,
			canonicalClinicClient,
		)
		canonicalDataProxyHandler.RegisterRoutes(r)
		log.Info(ctx, "Canonical DataProxy routes registered", zap.String("path", "/data-proxy/api/v1"))

		// SlowQuery Analyzer routes (基于 DataProxy Service)
		slowQueryAnalyzer := service.NewSlowQueryAnalyzer(dataProxySvc)
		slowQueryHandler := handlers.NewSlowQueryHandler(slowQueryAnalyzer, dataProxyCfg)
		slowQueryHandler.RegisterRoutes(r)
		log.Info(ctx, "SlowQuery Analyzer routes registered", zap.String("path", "/clinic-internal/healthcheck/sql/slowquery-analyze"))
	}

	// Metrics HealthCheck routes
	metricsHCSvc := service.NewMetricsHealthCheckService()
	metricsHCHandler := handlers.NewMetricsHealthCheckHandler(metricsHCSvc, dataProxyCfg)
	metricsHCHandler.RegisterRoutes(r)
	log.Info(ctx, "Metrics healthcheck routes registered", zap.String("path", "/clinic-internal/healthcheck/metrics/analyze"))

	// Metrics Query routes
	if dataProxyCfg.MetricsQuery.Enabled {
		metricsQuerySvc := service.NewMetricsQueryService(dataProxyCfg)
		metricsQueryHandler := handlers.NewMetricsQueryHandler(metricsQuerySvc)
		metricsQueryHandler.RegisterRoutes(r)
		canonicalMetricsHandler := handlers.NewCanonicalMetricsHandler(
			metricsQueryHandler,
			canonicalOwnershipRepository,
			canonicalClinicClient,
		)
		canonicalMetricsHandler.RegisterRoutes(r)
		log.Info(ctx, "Metrics query routes registered", zap.String("path", "/data-proxy/metrics"))
		log.Info(ctx, "Canonical metrics routes registered", zap.String("path", "/data-proxy/api/v1/orgs/:orgId/clusters/:clusterId/metrics"))

		serverlessParentMetricsSvc := service.NewServerlessParentMetricsService(dataProxyCfg)
		serverlessParentMetricsHandler := handlers.NewServerlessParentMetricsHandler(serverlessParentMetricsSvc)
		serverlessParentMetricsHandler.RegisterRoutes(r)
		log.Info(ctx, "Serverless parent metrics routes registered", zap.String("path", "/data-proxy/serverless-parent-metrics"))
	}

	// Loki Proxy routes
	lokiProxyService := service.NewLokiProxyService(dataProxyCfg)
	lokiHandler := handlers.NewLokiHandler(dataProxyCfg.ClinicAPIBase, lokiProxyService)
	lokiHandler.RegisterRoutes(r)
	canonicalBusinessLogsHandler := handlers.NewCanonicalBusinessLogsHandler(
		lokiProxyService,
		canonicalOwnershipRepository,
		canonicalClinicClient,
	)
	canonicalBusinessLogsHandler.RegisterRoutes(r)
	lokiHandler.RegisterCanonicalSystemLogRoutes(r)
	log.Info(ctx, "Canonical logs routes registered", zap.String("path", "/data-proxy/api/v1"))

	// AI HealthCheck Analyze routes
	aiSvc, err := service.NewAIService(dataProxyCfg)
	if err != nil {
		log.Error(ctx, "AI Analyze service init skipped", zap.Error(err))
	} else {
		aiHandler := handlers.NewAIHealthCheckHandler(aiSvc, dataProxyCfg)
		aiHandler.RegisterRoutes(r)
		log.Info(ctx, "AI healthcheck analyze routes registered",
			zap.String("path", "/clinic-internal/healthcheck/ai/analyze"))
	}
}
