package httpapi

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

	"commilitia.net/cdrop/internal/hub"
	"commilitia.net/cdrop/internal/jwtauth"
)

// maxSignalBytes caps the signaling body. WebRTC SDP offers/answers and bundled
// ICE candidates are at most a few KB; the payload is an opaque json.RawMessage
// with no other length check, so without this a single request could stream an
// unbounded body into memory (R4). 64 KiB leaves generous headroom for fat SDP.
const maxSignalBytes = 64 * 1024

type signalReq struct {
	To      string          `json:"to"`
	Payload json.RawMessage `json:"payload"`
}

// handleSignal forwards WebRTC offer/answer/ICE candidates between same-user devices.
// Body: { to: deviceName, payload: any-json }
// 204 if delivered to live SSE; 410 if peer offline.
func (s *Server) handleSignal(w http.ResponseWriter, r *http.Request) {
	claims, _ := jwtauth.ClaimsFromContext(r.Context())
	from, _ := jwtauth.DeviceNameFromContext(r.Context())

	r.Body = http.MaxBytesReader(w, r.Body, maxSignalBytes)
	var req signalReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		var mbe *http.MaxBytesError
		if errors.As(err, &mbe) {
			writeJSON(w, http.StatusRequestEntityTooLarge,
				map[string]string{"error": "signal payload too large"})
			return
		}
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid json"})
		return
	}
	if req.To == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing 'to'"})
		return
	}
	if req.To == from {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "cannot signal self"})
		return
	}

	delivered := s.hub.SendTo(claims.UserID, req.To, hub.Event{
		Type: "signal",
		Data: map[string]any{
			"from":    from,
			"payload": req.Payload,
		},
	})
	if !delivered {
		writeJSON(w, http.StatusGone, map[string]string{"error": "peer offline"})
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
