package profiler

import (
	"context"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/spf13/viper"
	"go.uber.org/zap"
)

type (
	httpParams struct {
		Key     string
		Viper   *viper.Viper
		Logger  *zap.Logger
		Handler http.Handler
	}

	httpServer struct {
		name        string
		started     *int32
		logger      *zap.Logger
		shutdownTTL time.Duration
		server      server
	}
)

func (h *httpServer) Start(ctx context.Context) {
	if h == nil {
		return
	}

	if !atomic.CompareAndSwapInt32(h.started, 0, 1) {
		h.logger.Info("http: already started",
			zap.String("server", h.name))
		return
	}

	go func() {
		if err := h.server.serve(ctx); err != nil {
			if err != http.ErrServerClosed {
				h.logger.Error("http: could not start server",
					zap.Error(err))
			}
		}
	}()
}

func (h *httpServer) Stop() {
	if h == nil {
		return
	}

	if !atomic.CompareAndSwapInt32(h.started, 1, 0) {
		h.logger.Info("http: already stopped",
			zap.String("server", h.name))
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), h.shutdownTTL)
	defer cancel()

	h.logger.Debug("http: try to stop server",
		zap.String("server", h.name))

	if err := h.server.shutdown(ctx); err != nil {
		h.logger.Error("http: could not stop server",
			zap.Error(err))
	}
}

const defaultShutdownTTL = 30 * time.Second

func newHTTPServer(p httpParams) *httpServer {
	var (
		address  string
		shutdown time.Duration
	)

	if address = p.Viper.GetString(p.Key + ".address"); address == "" {
		p.Logger.Info("Empty bind address, skip",
			zap.String("server", p.Key))
		return nil
	}
	if p.Handler == nil {
		p.Logger.Info("Empty handler, skip",
			zap.String("server", p.Key))
		return nil
	}

	p.Logger.Info("Create http.Server",
		zap.String("server", p.Key),
		zap.String("address", address))

	if shutdown = p.Viper.GetDuration(p.Key + ".shutdown_ttl"); shutdown <= 0 {
		shutdown = defaultShutdownTTL
	}

	return &httpServer{
		name:        p.Key,
		started:     new(int32),
		logger:      p.Logger,
		shutdownTTL: shutdown,
		server: newServer(params{
			Address: address,
			Name:    p.Key,
			Config:  p.Viper,
			Logger:  p.Logger,
			Handler: p.Handler,
		}),
	}
}