// Package mgmt provide management server. Provides API to get info about reproxy routes and settings
package mgmt

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

	log "github.com/go-pkgz/lgr"
	"github.com/go-pkgz/rest"

	"github.com/prometheus/client_golang/prometheus/promhttp"

	"github.com/umputun/reproxy/app/discovery"
)

//go:generate moq -out informer_mock.go -fmt goimports . Informer

// Server represents management server
type Server struct {
	Listen         string
	Informer       Informer
	Version        string
	AssetsLocation string
	AssetsWebRoot  string
	Metrics        *Metrics
}

// Informer wraps interface to get info about servers and mappers
type Informer interface {
	Mappers() (mappers []discovery.URLMapper)
}

// Run the lister and management router, activate rest server
func (s *Server) Run(ctx context.Context) error {
	log.Printf("[INFO] start management server on %s", s.Listen)

	handler := http.NewServeMux()
	handler.HandleFunc("/routes", s.routesCtrl())
	handler.Handle("/metrics", promhttp.Handler())
	h := rest.Wrap(handler,
		rest.Recoverer(log.Default()),
		rest.AppInfo("reproxy-mgmt", "umputun", s.Version),
		rest.Ping,
	)

	httpServer := http.Server{
		Addr:              s.Listen,
		Handler:           h,
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      10 * time.Second,
		IdleTimeout:       30 * time.Second,
	}

	go func() {
		<-ctx.Done()
		err := httpServer.Shutdown(context.Background())
		log.Printf("[WARN] mgmt server terminated, %v", err)
	}()

	return httpServer.ListenAndServe()
}

// routesCtrl - GET /routes, returns the list of all routes
func (s *Server) routesCtrl() func(w http.ResponseWriter, r *http.Request) {
	type resp struct {
		Route          string `json:"route,omitempty"`
		Destination    string `json:"destination,omitempty"`
		Server         string `json:"server"`
		MatchType      string `json:"match"`
		Provider       string `json:"provider"`
		AssetsLocation string `json:"assets_location,omitempty"`
		AssetsWebRoot  string `json:"assets_webroot,omitempty"`
		Ping           string `json:"ping,omitempty"`
	}

	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		res := map[string][]resp{}
		for _, mp := range s.Informer.Mappers() {
			res[mp.Server] = append(res[mp.Server], resp{Server: mp.Server, Provider: string(mp.ProviderID), Route: mp.SrcMatch.String(),
				Destination: mp.Dst, MatchType: mp.MatchType.String(), Ping: mp.PingURL})
		}
		if s.AssetsLocation != "" {
			res["*"] = append([]resp{{Server: "*", Provider: "system", MatchType: discovery.MTStatic.String(),
				AssetsLocation: s.AssetsLocation, AssetsWebRoot: s.AssetsWebRoot}}, res["*"]...)
		}
		rest.RenderJSON(w, res)
	}
}
