package api

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

	"drivervault/apiserver/internal/pb"
)

// writeJSON writes v as a JSON response with the given status code.
func writeJSON(w http.ResponseWriter, status int, v any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	if v == nil {
		return
	}
	if err := json.NewEncoder(w).Encode(v); err != nil {
		log.Printf("writeJSON: %v", err)
	}
}

// errorBody is the standard error envelope.
type errorBody struct {
	Error string `json:"error"`
}

// writeError writes a JSON error response.
func writeError(w http.ResponseWriter, status int, msg string) {
	writeJSON(w, status, errorBody{Error: msg})
}

// writeUpstreamDown reports that PocketBase could not be reached at all (a
// transport error, as opposed to PocketBase answering with an error status).
func writeUpstreamDown(w http.ResponseWriter, err error) {
	writeJSON(w, http.StatusBadGateway, map[string]any{
		"error":  "cannot reach PocketBase",
		"detail": err.Error(),
	})
}

// writePBError maps a PocketBase error from the typed CRUD helpers to an
// appropriate HTTP status.
func writePBError(w http.ResponseWriter, err error) {
	if apiErr, ok := err.(*pb.APIError); ok {
		status := apiErr.Status
		if status < 400 {
			status = http.StatusBadGateway
		}
		writeError(w, status, apiErr.Body)
		return
	}
	writeError(w, http.StatusBadGateway, err.Error())
}

// decodeJSON strictly decodes a request body, rejecting unknown fields.
func decodeJSON(r *http.Request, dest any) error {
	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()
	return dec.Decode(dest)
}
