package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

var (
	// ErrEmptyBody is returned when the request body is empty or nil.
	ErrEmptyBody = errors.New("request body is empty")
	// ErrInvalidJSON is returned when the request body contains invalid JSON.
	ErrInvalidJSON = errors.New("invalid JSON")
)

// DecodeJSON decodes JSON from the request body into v.
// Returns descriptive errors for common failure cases:
//   - nil or empty body → ErrEmptyBody
//   - malformed JSON → ErrInvalidJSON
//
// Usage:
//
//	if err := api.DecodeJSON(r, &req); err != nil {
//	    api.WriteBadRequest(w, r, "invalid request body")
//	    return
//	}
func DecodeJSON(r *http.Request, v any) error {
	if r.Body == nil {
		return ErrEmptyBody
	}

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(v); err != nil {
		if errors.Is(err, io.EOF) {
			return ErrEmptyBody
		}
		return fmt.Errorf("%w: %w", ErrInvalidJSON, err)
	}

	return nil
}

// DecodeJSONStrict decodes JSON from the request body into v.
// Rejects JSON containing fields not present in the target struct.
func DecodeJSONStrict(r *http.Request, v any) error {
	if r.Body == nil {
		return ErrEmptyBody
	}

	decoder := json.NewDecoder(r.Body)
	decoder.DisallowUnknownFields()

	if err := decoder.Decode(v); err != nil {
		if errors.Is(err, io.EOF) {
			return ErrEmptyBody
		}
		return fmt.Errorf("%w: %w", ErrInvalidJSON, err)
	}

	return nil
}
