package api

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

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

// errorBody / errorPayload preserve the existing wire envelope:
//
//	{"error": {"code": "...", "message": "..."}}
//
// Kept separate from the Subsonic envelope so native clients don't have
// to parse two shapes.
type errorBody struct {
	Error errorPayload `json:"error"`
}

type errorPayload struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

// writeErr emits the failure envelope for err. If err is or wraps
// *apierror.Error, its Status/Code/Message drive the response;
// otherwise it's mapped to a 500 server_error.
func writeErr(w http.ResponseWriter, err error) {
	apiErr := apierror.From(err)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(apiErr.Status)
	_ = json.NewEncoder(w).Encode(errorBody{Error: errorPayload{
		Code:    apiErr.Code,
		Message: apiErr.Message,
	}})
}

// writeErrWithLog logs the error at Error level and writes the response.
// Use for 500-class errors where the operator needs the cause in logs.
func writeErrWithLog(w http.ResponseWriter, logger *slog.Logger, msg string, err error) {
	if logger != nil {
		logger.Error(msg, "err", err)
	}
	writeErr(w, err)
}

// writeJSON emits a 2xx response with the given body JSON-encoded. Mirrors
// writeErr's shape so handlers have one shape for success and one for
// failure.
func writeJSON(w http.ResponseWriter, status int, body any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(body)
}
