package server

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"
)

func newDNSHandler(ctx context.Context, loop DNSLoop,
	warner warner) http.Handler {
	return &dnsHandler{
		ctx:    ctx,
		loop:   loop,
		warner: warner,
	}
}

type dnsHandler struct {
	ctx    context.Context //nolint:containedctx
	loop   DNSLoop
	warner warner
}

func (h *dnsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	r.RequestURI = strings.TrimPrefix(r.RequestURI, "/dns")
	switch r.RequestURI {
	case "/status": //nolint:goconst
		switch r.Method {
		case http.MethodGet:
			h.getStatus(w)
		case http.MethodPut:
			h.setStatus(w, r)
		default:
			http.Error(w, "method "+r.Method+" not supported", http.StatusBadRequest)
		}
	default:
		http.Error(w, "route "+r.RequestURI+" not supported", http.StatusBadRequest)
	}
}

func (h *dnsHandler) getStatus(w http.ResponseWriter) {
	status := h.loop.GetStatus()
	encoder := json.NewEncoder(w)
	data := statusWrapper{Status: string(status)}
	if err := encoder.Encode(data); err != nil {
		h.warner.Warn(err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (h *dnsHandler) setStatus(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var data statusWrapper
	if err := decoder.Decode(&data); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	status, err := data.getStatus()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	outcome, err := h.loop.ApplyStatus(h.ctx, status)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	encoder := json.NewEncoder(w)
	if err := encoder.Encode(outcomeWrapper{Outcome: outcome}); err != nil {
		h.warner.Warn(err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
}
