package server

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/getkin/kin-openapi/openapi3filter"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"go.uber.org/zap"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)

type DepsHTTP struct {
	Logger             *zap.Logger
	Swagger            *openapi3.T
	AuthenticationFunc openapi3filter.AuthenticationFunc
}

type HTTP struct {
	logger *zap.Logger
	server *http.Server
	echo   *echo.Echo
}

func NewHTTP(deps DepsHTTP) (*HTTP, errors.Error) {
	if deps.Logger == nil {
		return nil, errors.NewWithMessage("failed to init http server: logger is nil <NewHTTP>", errors.ErrInvalidArgs)
	}

	if deps.Swagger == nil {
		return nil, errors.NewWithMessage("failed to init http server: swagger is nil <NewHTTP>", errors.ErrInvalidArgs)
	}

	if deps.AuthenticationFunc == nil {
		return nil, errors.NewWithMessage("failed to init http server: AuthenticationFunc is nil <NewHTTP>", errors.ErrInvalidArgs)
	}

	echo := echo.New()

	echo.Use(middleware.Recover())
	echo.Use(swagger.CreateMiddleware(deps.Swagger, deps.AuthenticationFunc))

	return &HTTP{
		echo:   echo,
		logger: deps.Logger,
		server: &http.Server{
			Handler:        echo,
			MaxHeaderBytes: 1 << 20,
			ReadTimeout:    20 * time.Second,
			WriteTimeout:   20 * time.Second,
			IdleTimeout:    20 * time.Second,
		},
	}, nil
}

func (receiver *HTTP) Listen(address string) error {
	receiver.server.Addr = address

	return receiver.server.ListenAndServe()
}

func (receiver *HTTP) Run(config *models.ConfigurationHTTP) {
	connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port)
	startServerMessage := fmt.Sprintf("starting http server on %s", connectionString)

	receiver.logger.Info(startServerMessage)

	if err := receiver.Listen(connectionString); err != nil && err != http.ErrServerClosed {
		receiver.logger.Error("http listen error: ", zap.Error(err))
	}
}

func (receiver *HTTP) Stop(ctx context.Context) error {
	receiver.logger.Info("shutting down server...")

	if err := receiver.server.Shutdown(ctx); err != nil {
		return fmt.Errorf("failed to shutdown server: %w", err)
	}

	return nil
}

func (receiver *HTTP) Register(api *swagger.API) *HTTP {
	swagger.RegisterHandlers(receiver.echo, api)

	return receiver
}
