package main

import (
	"flag"
	"strings"
	"time"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tidbcloud/metrics-server/central/config"
	"github.com/tidbcloud/metrics-server/central/metrics"
	"github.com/tidbcloud/metrics-server/cmd"
	"github.com/tidbcloud/metrics-server/internal/rest"
	"github.com/tidbcloud/metrics-server/internal/service/query"
	ginprometheus "github.com/zsais/go-gin-prometheus"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	defaultConfigPath = "/etc/metrics/config.yaml"
	configPath        = flag.String("config-path", defaultConfigPath, "metrics configuration file path")
)

func main() {
	flag.Parse()

	if err := config.InitConfig(*configPath); err != nil {
		log.Fatal("Initialize configuration failed", zap.Error(err))
	}

	setupLogger()
	// Init shoot cache.
	monitoringService := query.NewManager()

	cmd.InitMetadbDriver()

	r := gin.Default()

	// prometheus
	prometheus.MustRegister(metrics.O11yServerRequestErrCnt, metrics.O11yServerRequestDuration, metrics.O11yServerQueryRequest)

	p := ginprometheus.NewPrometheus("gin")
	p.ReqCntURLLabelMappingFn = func(c *gin.Context) string {
		url := c.Request.URL.Path
		for _, p := range c.Params {
			if p.Key == "cluster_id" {
				url = strings.Replace(url, p.Value, ":cluster_id", 1)
				break
			}
			if p.Key == "project_id" {
				url = strings.Replace(url, p.Value, ":project_id", 1)
				break
			}
		}
		return url
	}
	p.Use(r)

	r.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"PUT", "PATCH", "GET", "POST", "DELETE"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		AllowOriginFunc: func(origin string) bool {
			return true
		},
		MaxAge: 12 * time.Hour,
	}))

	r.GET("/ping", rest.PingHandler)    // for health check in k8s
	r.GET("/healthz", rest.PingHandler) // for health check in k8s

	publicGroup := r.Group("/public_api/v1")
	{
		// TODO: this will be dropeed after customer migrated to new project level metrics
		publicGroup.GET("/clusters/:cluster_id/monitoring/measurements", rest.BuildMetricsHandler(monitoringService))
		// TODO: use a middleware after customer migrated to new project level metrics
		publicGroup.GET("/projects/:project_id/metrics", rest.HandleProjectMetrics(monitoringService))
	}

	// Listen and serve on 0.0.0.0:8080 by default.
	log.Fatal("failed to start metrics server", zap.Error(r.Run(config.Global().ServerConfig.MetricsListenAddr)))
}

func setupLogger(opts ...zap.Option) {
	cfg := &log.Config{
		Level:  config.Global().ServerConfig.LogLevel,
		Format: "json",
	}
	gl, props, err := log.InitLogger(cfg, append(opts, zap.AddStacktrace(zapcore.ErrorLevel))...)
	if err != nil {
		log.Error("failed init new logger, use default logger", zap.Error(err))
	}
	log.ReplaceGlobals(gl, props)
}
