package validjson

import (
	"context"
	"encoding/json"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
	"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)

const Key = "generic/valid_json"
const ReasonCodeInvalidJSON = "invalid_json"

type Options struct{}

type Validator struct{}

var _ contracts.SerializedValidator = (*Validator)(nil)

func New(Options) *Validator { return &Validator{} }

func (v *Validator) Name() string { return Key }

func (v *Validator) ExecutionClass() contracts.ExecutionClass {
	return contracts.ExecutionClassDeterministic
}

func (v *Validator) Validate(_ context.Context, req contracts.SerializedValidationRequest) (contracts.ValidationResult, error) {
	return validate(req.Content), nil
}

func validate(content []byte) contracts.ValidationResult {
	if !json.Valid(content) {
		return contracts.ValidationResult{Approved: false, ReasonCode: ReasonCodeInvalidJSON, Message: "payload is not valid JSON", CorrectionGuidance: "Return one complete replacement response encoded as valid JSON."}
	}
	return contracts.ValidationResult{Approved: true}
}

func Spec() pipeline.ValidatorSpec {
	return pipeline.ValidatorSpec{Key: Key, ExecutionClass: contracts.ExecutionClassDeterministic}
}

func Register(registry *pipeline.ValidatorRegistry) error {
	return pipeline.RegisterSerializedValidatorBuilder(registry, pipeline.SerializedValidatorSpec{
		ValidatorSpec: Spec(), SupportsChunks: true, SupportsArtifacts: true,
	}, validateOptions, func(request pipeline.BuildRequest) (contracts.SerializedValidator, error) {
		options, err := DecodeOptions(request.Options)
		if err != nil {
			return nil, err
		}
		return New(options), nil
	})
}

func DecodeOptions(options map[string]any) (Options, error) {
	if err := pipeline.RejectUnknownOptions(options); err != nil {
		return Options{}, err
	}
	return Options{}, nil
}

func validateOptions(options map[string]any) error { _, err := DecodeOptions(options); return err }
