package main

import (
	"fmt"
	"net/http"

	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"

	ctrl "github.com/tidbcloud/devops/pkg/controller"
	ctrlcomm "github.com/tidbcloud/devops/pkg/controller/common"
	jmpgwctrl "github.com/tidbcloud/devops/pkg/controller/jump/gateway"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
	metrics "github.com/tidbcloud/devops/pkg/infra/metrics/jump-gateway"
)

func run(conf *config.JumpGatewayConfig) error {
	auth, err := jmpgwctrl.NewAuth(conf.APIServer.Auth)
	if err != nil {
		return fmt.Errorf("failed to build auth: %w", err)
	}

	eng := gin.New(
		func(e *gin.Engine) {
			e.ContextWithFallback = true
			e.ForwardedByClientIP = true
		},
	)
	if err := eng.SetTrustedProxies(conf.APIServer.TrustedProxies); err != nil {
		return fmt.Errorf("set trusted proxies failed: %w", err)
	}

	// base middlewares
	logMW := &ctrlcomm.LogMetricMiddlewareBuilder{
		Request: ctrlcomm.RequestLoggerOptions{
			Logger: log.Logger.WithName("APIServer"),
			FilterFunc: func(c *gin.Context) bool {
				if c.Request.Method == http.MethodOptions {
					return true
				}
				if c.FullPath() == "/healthz" || c.FullPath() == "/metrics" {
					return true
				}
				return false
			},
		},
		Access: &ctrlcomm.AccessLoggerOptions{
			Logger: log.Logger.WithName("APIServer/Access"), // be same as request logger
		},
		Metric: &metrics.Gin,
	}
	eng.Use(
		otelgin.Middleware("jump-gateway"),
		logMW.Build(),
		ctrl.RecoveryMiddleware(log.Logger.WithName("APIServer")),
	)

	// base api
	eng.GET("/healthz", gin.WrapF(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(200) }))
	eng.GET("/metrics", gin.WrapH(promhttp.Handler()))
	pprof.Register(eng) // /debug/pprof/xxxx

	// routes
	rootRoute := eng.Group("/jump-gateway")
	rootRoute.Use(
		auth.Middleware,
	)
	jmpGWCtrl := jmpgwctrl.NewJumpGatewayController(log.Logger)
	jmpGWCtrl.RegisterAPI(rootRoute)

	// run server
	server := &http.Server{
		Addr:              conf.APIServer.ListenAddr,
		Handler:           eng,
		ReadHeaderTimeout: conf.APIServer.ReadHeaderTimeout,
	}

	return server.ListenAndServe()
}
