package api

import (
	"encoding/json"
	"errors"
	"net/http"

	"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
	"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
	"git.fabledsword.com/bvandeusen/minstrel/internal/netsettings"
)

type networkSettingsResp struct {
	TrustedProxyHops int `json:"trusted_proxy_hops"`
	MaxHops          int `json:"max_hops"`
	// DetectedClientIP is what the CURRENT setting resolves this very request
	// to. It's the difference between a number the operator has to reason
	// about and one they can verify: set the value, reload, and check the
	// address matches the machine you're sitting at.
	DetectedClientIP string `json:"detected_client_ip"`
	// ForwardedChain is the raw X-Forwarded-For as received, so an operator
	// whose detected address looks wrong can see how many hops actually
	// arrived and count them rather than guess.
	ForwardedChain string `json:"forwarded_chain"`
	RemoteAddr     string `json:"remote_addr"`
}

type updateNetworkSettingsReq struct {
	TrustedProxyHops int `json:"trusted_proxy_hops"`
}

func (h *handlers) handleGetNetworkSettings(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, http.StatusOK, h.networkSettingsPayload(r))
}

func (h *handlers) handleUpdateNetworkSettings(w http.ResponseWriter, r *http.Request) {
	var req updateNetworkSettingsReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeErr(w, apierror.BadRequest("invalid_body", "malformed JSON"))
		return
	}
	if err := h.netSettings.SetHops(r.Context(), req.TrustedProxyHops); err != nil {
		if errors.Is(err, netsettings.ErrHopsOutOfRange) {
			writeErr(w, apierror.BadRequest("invalid_hops", err.Error()))
			return
		}
		writeErrWithLog(w, h.logger, "admin network: update failed", apierror.Internal(err))
		return
	}
	// Echo the payload recomputed under the NEW value, so the card can show
	// immediately what the change did to this request's own address rather
	// than making the operator reload to find out.
	writeJSON(w, http.StatusOK, h.networkSettingsPayload(r))
}

func (h *handlers) networkSettingsPayload(r *http.Request) networkSettingsResp {
	hops := h.netSettings.Hops()
	return networkSettingsResp{
		TrustedProxyHops: hops,
		MaxHops:          netsettings.MaxTrustedProxyHops,
		DetectedClientIP: auth.ClientIP(r, hops),
		ForwardedChain:   r.Header.Get("X-Forwarded-For"),
		RemoteAddr:       r.RemoteAddr,
	}
}
