package outboundintentv1

import (
	"bytes"
	"encoding/json"
)

// DecodeUpsertDesiredStateV2 validates the closed provider-neutral desired
// state and its create/update constraint. It never accepts a remote identity,
// credential, endpoint, provider response or retry state from Runtime.
func DecodeUpsertDesiredStateV2(intent Intent) (UpsertDesiredStateV2, error) {
	if err := validateIntent(intent); err != nil {
		return UpsertDesiredStateV2{}, invalid()
	}
	if intent.Operation != OperationUpsert || intent.PayloadFormatVersion != 2 {
		return UpsertDesiredStateV2{}, invalid()
	}
	decoder := json.NewDecoder(bytes.NewReader(intent.CanonicalPayload))
	decoder.DisallowUnknownFields()
	decoder.UseNumber()
	var payload UpsertDesiredStateV2
	if err := decoder.Decode(&payload); err != nil {
		return UpsertDesiredStateV2{}, invalid()
	}
	if err := requireJSONEOF(decoder); err != nil {
		return UpsertDesiredStateV2{}, invalid()
	}
	if payload.Version != 2 || !payload.WriteDisposition.Valid() {
		return UpsertDesiredStateV2{}, invalid()
	}
	v1 := UpsertDesiredStateV1{
		Version: 1, TargetObject: payload.TargetObject, BodyRoot: payload.BodyRoot,
		MappedFields: payload.MappedFields, Associations: payload.Associations,
	}
	if err := validateUpsertDesiredStateV1(v1); err != nil {
		return UpsertDesiredStateV2{}, invalid()
	}
	canonical, err := json.Marshal(payload)
	if err != nil || !bytes.Equal(canonical, intent.CanonicalPayload) {
		return UpsertDesiredStateV2{}, invalid()
	}
	return payload, nil
}
