package app

import (
	"context"
	"errors"
	"net"
	"net/http"
	"time"
)

type ListenerFactory func(network, address string) (net.Listener, error)

type Server struct {
	server *http.Server
	listen ListenerFactory
}

func NewServer(listenAddr string, handler http.Handler, listenerFactory ListenerFactory) *Server {
	if handler == nil {
		handler = NewAPIHandler("", ActionSet{})
	}
	server := &Server{
		server: &http.Server{
			Addr:    listenAddr,
			Handler: handler,
		},
		listen: net.Listen,
	}
	if listenerFactory != nil {
		server.listen = listenerFactory
	}
	return server
}

func (s *Server) Addr() string {
	return s.server.Addr
}

func (s *Server) Run(ctx context.Context) error {
	listener, err := s.listen("tcp", s.server.Addr)
	if err != nil {
		return err
	}
	return s.Serve(ctx, listener)
}

func (s *Server) Serve(ctx context.Context, listener net.Listener) error {
	errCh := make(chan error, 1)
	go func() {
		err := s.server.Serve(listener)
		if errors.Is(err, http.ErrServerClosed) {
			err = nil
		}
		errCh <- err
	}()

	select {
	case <-ctx.Done():
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := s.server.Shutdown(shutdownCtx); err != nil {
			return err
		}
		return <-errCh
	case err := <-errCh:
		return err
	}
}
