package outboundintentv1

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
)

const (
	// ResourceNoEffectResultFormatV1 selects the resource no-effect variant in
	// CommitResultRequest. Envelope value 1 remains the existing mail variant;
	// the resource variant's own payload version is 1.
	ResourceNoEffectResultFormatV1 uint32 = 2
	MaxResourceNoEffectResultBytes        = 1024
)

type ResourceNoEffectOutcome string

const ResourceOutcomeNoEffect ResourceNoEffectOutcome = "NO_EFFECT"

type ResourceNoEffectReason string

const (
	ResourceNoEffectRejectedBeforeDispatch ResourceNoEffectReason = "REJECTED_BEFORE_DISPATCH"
	ResourceNoEffectDefinitiveNoEffect     ResourceNoEffectReason = "DEFINITIVE_NO_EFFECT"
)

// ResourceNoEffectResultV1 settles an accepted UPSERT or DELETE whose
// Integration owner has durably proved that no external effect occurred and
// no unresolved external effect remains across any attempt. It does not
// authorize metadata projection or establish a remote identity. Provider
// timeout, lease expiry, uncertain delivery and partially applied operations
// cannot produce this result, even when the command will not be retried.
//
// REJECTED_BEFORE_DISPATCH requires that no attempt reached provider I/O.
// DEFINITIVE_NO_EFFECT requires affirmative evidence that every dispatched
// attempt produced no external effect. Integration owns that evidence and
// must durably prohibit further dispatch before handing the result back.
type ResourceNoEffectResultV1 struct {
	Version uint32                  `json:"version"`
	Outcome ResourceNoEffectOutcome `json:"outcome"`
	Reason  ResourceNoEffectReason  `json:"reason"`
}

// CanonicalResourceNoEffectResultV1 returns the immutable bytes and digest to
// persist before invoking CommitResult. Retries reuse these exact values.
func CanonicalResourceNoEffectResultV1(result ResourceNoEffectResultV1) ([]byte, string, error) {
	if validateResourceNoEffectResultV1(result) != nil {
		return nil, "", invalid()
	}
	canonical, err := json.Marshal(result)
	if err != nil {
		return nil, "", invalid()
	}
	sum := sha256.Sum256(canonical)
	return canonical, "sha256:" + hex.EncodeToString(sum[:]), nil
}

func DecodeResourceNoEffectResultV1(raw []byte) (ResourceNoEffectResultV1, error) {
	if len(raw) == 0 || len(raw) > MaxResourceNoEffectResultBytes {
		return ResourceNoEffectResultV1{}, invalid()
	}
	decoder := json.NewDecoder(bytes.NewReader(raw))
	decoder.DisallowUnknownFields()
	var result ResourceNoEffectResultV1
	if decoder.Decode(&result) != nil || requireJSONEOF(decoder) != nil {
		return ResourceNoEffectResultV1{}, invalid()
	}
	canonical, _, err := CanonicalResourceNoEffectResultV1(result)
	if err != nil || !bytes.Equal(canonical, raw) {
		return ResourceNoEffectResultV1{}, invalid()
	}
	return result, nil
}

func validateResourceNoEffectResultV1(result ResourceNoEffectResultV1) error {
	if result.Version != 1 || result.Outcome != ResourceOutcomeNoEffect {
		return invalid()
	}
	switch result.Reason {
	case ResourceNoEffectRejectedBeforeDispatch, ResourceNoEffectDefinitiveNoEffect:
		return nil
	default:
		return invalid()
	}
}
