package http

import (
	"context"
	"log"
	"net"
	"net/http"
	"os"
	"time"

	"github.com/julienschmidt/httprouter"
)

type HttpServer struct {
	server *http.Server
	router *httprouter.Router
}

func NewHttpServer() *HttpServer {
	r := httprouter.New()
	return &HttpServer{
		server: &http.Server{
			ReadTimeout:  3 * time.Second,
			WriteTimeout: 3 * time.Second,
			Handler:      r,
		},
		router: r,
	}
}

func (s *HttpServer) GET(path string, handler http.HandlerFunc) {
	s.router.Handler(http.MethodGet, path, handler)
}

func (s *HttpServer) POST(path string, handler http.HandlerFunc) {
	s.router.Handler(http.MethodPost, path, handler)
}

func (s *HttpServer) PATCH(path string, handler http.HandlerFunc) {
	s.router.Handler(http.MethodPatch, path, handler)
}

func (s *HttpServer) DELETE(path string, handler http.HandlerFunc) {
	s.router.Handler(http.MethodDelete, path, handler)
}

func (s *HttpServer) ServeStatic(path string, fsys http.FileSystem) {
	s.router.ServeFiles(path, fsys)
}

func (s *HttpServer) SetNotFoundHandler(handler http.HandlerFunc) {
	s.router.NotFound = handler
}

// GetURLParam wrapper around underlying router for getting URL parameters.
func GetURLParam(r *http.Request, param string) string {
	return httprouter.ParamsFromContext(r.Context()).ByName(param)
}

func (s *HttpServer) Start(network, address string) error {
	listener, err := net.Listen(network, address)
	if err != nil {
		return err
	}

	if listener.Addr().Network() == "unix" {
		os.Chmod(address, 0777)
	}

	go func() {
		if err = s.server.Serve(listener); err != nil && err != http.ErrServerClosed {
			log.Fatalln(err)
		}
	}()

	return nil
}

func (s *HttpServer) Stop() error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := s.server.Shutdown(ctx); err != nil {
		return err
	}

	return nil
}
