package controller

import (
	"context"
	"fmt"
	"net/http"
	"reflect"
	"sync"

	"github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/config"

	"github.com/gin-gonic/gin"
	"github.com/go-logr/logr"
	"go.uber.org/dig"
)

type Suite struct {
	// Router is a router for public API.
	// It has auth middleware and registers all API in the Kong.
	Router gin.IRouter
	// InternalRouter is a router for internal API that is accessed in the private network.
	// It does not have auth middleware.
	InternalRouter gin.IRouter
	// ExternalRouter is a router for public API but does not have auth middleware.
	ExternalRouter gin.IRouter
}

type Controller interface {
	RegisterAPI(suite *Suite) error
}

type APIServerParams struct {
	dig.In

	Conf        *config.RootConfig
	Logger      logr.Logger
	Controllers []Controller `group:"controller"`
}

type APIServer struct {
	conf config.APIServerConfig

	logger      logr.Logger
	wg          *sync.WaitGroup
	eng         *gin.Engine
	server      *http.Server
	controllers []Controller
}

func NewAPIServer(p APIServerParams) (*APIServer, error) {
	var err error
	s := &APIServer{
		conf:        p.Conf.APIServer,
		logger:      p.Logger.WithName("APIServer"),
		wg:          &sync.WaitGroup{},
		controllers: p.Controllers,
	}

	s.eng = gin.New(
		func(e *gin.Engine) {
			e.ContextWithFallback = true
		},
	)

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

	err = s.initRoute()
	if err != nil {
		return nil, fmt.Errorf("init api server failed: %s", err)
	}

	return s, nil
}

// initRoute register all http api routes.
//
//	@title			Devops API
//	@version		1.0
//	@description	devops server
func (s *APIServer) initRoute() error {
	// middlewares
	// s.eng.Use(
	// 	otelgin.Middleware("devops-server"),
	// 	LoggerMetricsMiddleware(s.logger.WithName("Gin"), false),
	// 	RecoveryMiddleware(s.logger.WithName("Gin")),
	// )

	// route := s.eng.Group("")
	//route.Use(AuthMiddleware(s.auth))

	suite := &Suite{
		Router: s.eng,
		// InternalRouter: s.eng.Group("/api/v1/devops-internal"),
		// ExternalRouter: s.eng.Group("/api/v1/devops-external"),
	}

	for _, ctrl := range s.controllers {
		s.logger.Info("register a controller", "controller", reflect.TypeOf(ctrl).String())
		err := ctrl.RegisterAPI(suite)
		if err != nil {
			return fmt.Errorf("register controller failed: %s", err)
		}
	}
	return nil
}

// Run runs the API server and blocks until the API server exits.
func (s *APIServer) Run() error {
	s.wg.Add(1)
	defer s.wg.Done()

	err := s.server.ListenAndServe()
	if err != http.ErrServerClosed {
		s.logger.Error(err, "api server is shutdown but got a error")
		return fmt.Errorf("failed to run api server: %s", err)
	}

	s.logger.Info("api server is shutdown")
	return nil
}

// Shutdown stops the API server and wait for server to exit.
func (s *APIServer) Shutdown(ctx context.Context) error {
	s.logger.Info("api server is shutting down by caller")

	err := s.server.Shutdown(ctx)
	s.wg.Wait()

	return err
}
