// Package candidatejson provides strict JSON mechanics for D&D artifact
// candidates before artifact-specific validation.
package candidatejson

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
)

// EncodeCandidate encodes a typed candidate with an artifact-specific error
// label.
func EncodeCandidate[T any](label string, value T) ([]byte, error) {
	content, err := json.Marshal(value)
	if err != nil {
		return nil, fmt.Errorf("encode %s: %w", label, err)
	}
	return content, nil
}

// DecodeCandidate decodes exactly one typed JSON value while rejecting unknown
// fields and trailing values with an artifact-specific error label.
func DecodeCandidate[T any](label string, content []byte) (T, error) {
	decoder := json.NewDecoder(bytes.NewReader(content))
	decoder.DisallowUnknownFields()
	var value T
	if err := decoder.Decode(&value); err != nil {
		var zero T
		return zero, fmt.Errorf("decode %s: %w", label, err)
	}
	var trailing any
	if err := decoder.Decode(&trailing); err != io.EOF {
		var zero T
		return zero, fmt.Errorf("decode %s: multiple JSON values", label)
	}
	return value, nil
}
