package service

import (
	"context"
	"crypto/tls"
	"fmt"
	"net/http"
        "net"
	"time"
        "path/filepath"

	"webserv/internal/handler"
	"webserv/pkg/auxtool/auxgin"
	"webserv/pkg/logger"

	"github.com/gin-gonic/gin"
)

const (
	httpTimeout = 360
)

type ServiceConfig struct {
	AssetsDir string
	PortNum  int
	Hostname string
	X509Cert []byte
	X509Key  []byte

	Handler  *handler.Handler
}

type Service struct {
	assetsDir string

	hand   *handler.Handler
	hsrv   *http.Server
	log    *logger.Logger
	engine *gin.Engine
        listen net.Listener

	portnum  int
	hostname string
	x509cert []byte
	x509key  []byte
}

func NewService(conf *ServiceConfig) (*Service, error) {
	var err error
	svc := &Service{
                assetsDir: conf.AssetsDir,
		hand:     conf.Handler,
		portnum:  conf.PortNum,
		hostname: conf.Hostname,
		x509cert: conf.X509Cert,
		x509key:  conf.X509Key,
	}
	svc.log = logger.NewLogger("service")
	return svc, err
}

func (svc *Service) Build() error {
	var err error
	svc.log.Debugf("Build service")

	gin.SetMode(gin.ReleaseMode)
	gin.DisableConsoleColor()

	svc.engine = gin.New()
	svc.engine.Use(gin.Recovery())
	svc.engine.Use(auxgin.CorsMiddleware())
	svc.engine.Use(auxgin.LogMiddleware())

        apiGroup := svc.engine.Group("api")
        {
                auxGroup := apiGroup.Group("v1")
                {
                        sessionGroup := auxGroup.Group("user")
                        sessionGroup.POST("create", svc.hand.CreateUser)
                        sessionGroup.POST("delete", svc.hand.DeleteUser)
                }

                docsGroup := apiGroup.Group("docs")
                swaggerUIPath := filepath.Join(svc.assetsDir, "swagger")
                docsGroup.Static("", swaggerUIPath)
        }
	noRouteFunc := func(gctx *gin.Context) {
		gctx.Status(http.StatusNotFound)
		return
	}
	svc.engine.NoRoute(noRouteFunc)

        const useTLS = false
        if useTLS {
                tlsCert, err := tls.X509KeyPair(svc.x509cert, svc.x509key)
                if err != nil {
                        return err
                }

                tlsConfig := tls.Config{
                        Certificates:       []tls.Certificate{tlsCert},
                        ClientAuth:         tls.NoClientCert,
                        InsecureSkipVerify: true,
                }

                listenAddress := fmt.Sprintf(":%d", svc.portnum)
                svc.listen, err = tls.Listen("tcp", listenAddress, &tlsConfig)
                if err != nil {
                        return err
                }

        } else {
                listenAddress := fmt.Sprintf(":%d", svc.portnum)
                svc.listen, err = net.Listen("tcp", listenAddress)
                if err != nil {
                        return err
                }
        }
        svc.hsrv = &http.Server{
                Handler: svc.engine,
        }
	return err
}

func (svc *Service) Router() *gin.Engine {
	return svc.engine
}

func (svc *Service) Handler() *handler.Handler {
	return svc.hand
}

func (svc *Service) Run() error {
	var err error
	for _, route := range svc.engine.Routes() {
		svc.log.Debugf("The route is registered: %s %s", route.Method, route.Path)
	}
        err = svc.hsrv.Serve(svc.listen)
        if err != nil {
                return err
        }
	return err
}

func (svc *Service) Stop() {
	svc.log.Infof("Stopping service")
	if svc.hsrv != nil {
		downWaiting := 5 * time.Second
		ctx, _ := context.WithTimeout(context.Background(), downWaiting)
		svc.hsrv.Shutdown(ctx)
	}
}
