package httputil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"

	"github.com/OffchainLabs/prysm/v7/api"
)

type HasStatusCode interface {
	StatusCode() int
}

// DefaultJsonError is a JSON representation of a simple error value, containing only a message and an error code.
type DefaultJsonError struct {
	Message string `json:"message"`
	Code    int    `json:"code"`
}

func (e *DefaultJsonError) StatusCode() int {
	return e.Code
}

func (e *DefaultJsonError) Error() string {
	return fmt.Sprintf("HTTP request unsuccessful (%d: %s)", e.Code, e.Message)
}

// WriteJson writes the response message in JSON format.
func WriteJson(w http.ResponseWriter, v any) {
	w.Header().Set("Content-Type", api.JsonMediaType)
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(v); err != nil {
		log.WithError(err).Error("Could not write response message")
	}
}

// WriteSsz writes the response message in ssz format
func WriteSsz(w http.ResponseWriter, respSsz []byte) {
	w.Header().Set("Content-Length", strconv.Itoa(len(respSsz)))
	w.Header().Set("Content-Type", api.OctetStreamMediaType)
	w.WriteHeader(http.StatusOK)
	if _, err := io.Copy(w, io.NopCloser(bytes.NewReader(respSsz))); err != nil {
		log.WithError(err).Error("Could not write response message")
	}
}

// WriteError writes the error by manipulating headers and the body of the final response.
func WriteError(w http.ResponseWriter, errJson HasStatusCode) {
	j, err := json.Marshal(errJson)
	if err != nil {
		log.WithError(err).Error("Could not marshal error message")
		return
	}
	w.Header().Set("Content-Length", strconv.Itoa(len(j)))
	w.Header().Set("Content-Type", api.JsonMediaType)
	w.WriteHeader(errJson.StatusCode())
	if _, err := io.Copy(w, io.NopCloser(bytes.NewReader(j))); err != nil {
		log.WithError(err).Error("Could not write error message")
	}
}
