// Package eventcontract is the neutral shared contract package for the
// producer-to-public Run event schema bijection. It is imported by BOTH the
// producing boundary (internal/workflow/run and orchestrator) and the closed
// public projector (internal/api/workflow/run), so producer additions/removals,
// unknown public fields/kinds, and failure-capability drift fail CI without a
// duplicated API-only allowlist. There is no import cycle: this package imports
// nothing from either side.
package eventcontract

// ProducerEventFieldSet declares the exact top-level payload fields one
// producer event kind emits, plus its failure capability and exact failure
// namespace.
type ProducerEventFieldSet struct {
	// Kind is the event type string.
	Kind string
	// Fields is the exact ordered set of non-failure top-level payload fields.
	Fields []string
	// FailureCapable reports whether the producer embeds a failure contract.
	FailureCapable bool
	// FailureFields is the exact failure-namespace field set for
	// FailureCapable kinds.
	FailureFields []string
}

// HasField reports whether field is a declared non-failure or failure field.
func (set ProducerEventFieldSet) HasField(field string) bool {
	for _, declared := range set.Fields {
		if declared == field {
			return true
		}
	}
	if set.FailureCapable {
		for _, declared := range set.FailureFields {
			if declared == field {
				return true
			}
		}
	}
	return false
}

// IsFailureField reports whether field is a declared failure-namespace field.
func (set ProducerEventFieldSet) IsFailureField(field string) bool {
	if !set.FailureCapable {
		return false
	}
	for _, declared := range set.FailureFields {
		if declared == field {
			return true
		}
	}
	return false
}

// FailureEventFieldSet is the exact failure-namespace field set emitted by the
// shared failure contract (kernel FailureEvent and the orchestrator
// failureEvent/callbackCompletionEventPayload builders).
var FailureEventFieldSet = []string{
	"code", "stage", "path", "problem", "hint", "retryable", "diagnostics", "truncation", "workItem",
}

// KernelEventFieldSet is the closed non-failure field vocabulary of the kernel
// Event struct (internal/workflow/run/state.go). Every kernel event kind's
// declared fields are a subset of this.
var KernelEventFieldSet = []string{
	"kind", "activationId", "nodeId", "agentId", "port", "scopeId",
}

// ProducerEventFieldSets is the complete producer-owned registry of exact event
// field sets for EVERY producer event kind reaching Run logs/events: the kernel
// reducer/engine/fan-out/loop/parallel/branch producers, the orchestrator
// dispatcher/callback/approval/retry/timeout producers, the store cancellation
// producer, and the replay producer.
var ProducerEventFieldSets = []ProducerEventFieldSet{
	// Invocation acceptance ledger entry (sequence 1) written by the store
	// invocation-acceptance producer (internal/store/workflow/invocation). It is
	// NOT emitted through the kernel/dispatcher effectEvent builders, so it is
	// declared here as the producer-owned vocabulary entry the public Run
	// events/log surfaces validate against. The public-safe context field is the
	// enum-validated `status`; the remaining payload keys (invocationId,
	// bindingRevisionId, planVersionId, inputDigest) are operational metadata
	// scrubbed by the typed public projector.
	{Kind: "run_accepted", Fields: []string{"status"}},

	// Kernel reducer / engine lifecycle.
	{Kind: "run_started", Fields: []string{"kind"}},
	{Kind: "run_succeeded", Fields: []string{"kind", "activationId", "nodeId"}},
	{Kind: "run_canceled", Fields: []string{"kind", "reason", "actor"}},
	{Kind: "run_cancel_replayed", Fields: []string{"kind"}},
	{Kind: "run_failed", Fields: []string{"kind", "activationId", "nodeId"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "activation_started", Fields: []string{"kind", "activationId"}},
	{Kind: "activation_created", Fields: []string{"kind", "activationId", "nodeId", "scopeId"}},
	{Kind: "activation_completed", Fields: []string{"kind", "activationId", "nodeId", "port"}},
	{Kind: "activation_failed", Fields: []string{"kind", "activationId", "nodeId"}, FailureCapable: true, FailureFields: FailureEventFieldSet},

	// Kernel branch / parallel / loop / fan-out scope events.
	{Kind: "branch_arrived", Fields: []string{"kind", "scopeId", "port"}},
	{Kind: "branch_failed", Fields: []string{"kind", "activationId", "nodeId", "scopeId"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "parallel_opened", Fields: []string{"kind", "activationId", "scopeId"}},
	{Kind: "parallel_resolved", Fields: []string{"kind", "scopeId", "port"}},
	{Kind: "loop_opened", Fields: []string{"kind", "activationId", "scopeId"}},
	{Kind: "loop_continued", Fields: []string{"kind", "activationId", "scopeId"}},
	{Kind: "loop_completed", Fields: []string{"kind", "activationId", "scopeId"}},
	{Kind: "loop_broken", Fields: []string{"kind", "activationId", "scopeId"}},
	{Kind: "fan_out_opened", Fields: []string{"kind", "activationId", "nodeId", "scopeId"}},
	{Kind: "fan_out_item_arrived", Fields: []string{"kind", "scopeId"}},
	{Kind: "fan_out_item_started", Fields: []string{"kind", "scopeId"}},
	{Kind: "fan_out_item_responder_activated", Fields: []string{"kind", "activationId", "nodeId", "agentId", "scopeId"}},
	{Kind: "fan_out_item_blocked", Fields: []string{"kind", "scopeId"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "fan_out_item_failed", Fields: []string{"kind", "scopeId"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "fan_out_resolved", Fields: []string{"kind", "scopeId", "port"}},

	// Kernel effect request events.
	{Kind: "effect_requested", Fields: []string{"kind", "activationId", "nodeId"}},
	{Kind: "effect_waiting_for_responder_capacity", Fields: []string{"effectId", "activationId", "nodeId", "kind", "waitingReason"}},
	{Kind: "effect_responder_selected", Fields: []string{"effectId", "activationId", "nodeId", "kind", "waitingReason"}},
	{Kind: "effect_responder_promoted", Fields: []string{"effectId", "activationId", "nodeId", "kind", "waitingReason"}},
	{Kind: "wait_requested", Fields: []string{"kind", "activationId", "nodeId"}},
	{Kind: "approval_requested", Fields: []string{"kind", "activationId", "nodeId"}},

	// Task-watch / completion-replay events.
	{Kind: "callback_responder_observed", Fields: []string{"kind", "activationId", "nodeId", "agentId"}},
	{Kind: "callback_task_watch_projection_restored", Fields: []string{"kind", "activationId", "nodeId"}},
	{Kind: "completion_replayed", Fields: []string{"kind", "activationId", "port"}},

	// orchestrator dispatcher / callback / approval / retry / timeout events.
	{Kind: "wait_completed", Fields: []string{"effectId", "activationId", "nodeId", "kind"}},
	{Kind: "message_sent", Fields: []string{"effectId", "activationId", "nodeId", "kind", "messageId", "deliveryScope", "threadScope"}},
	{Kind: "message_callback_awaiting", Fields: []string{"effectId", "activationId", "nodeId", "kind", "messageId", "wakeAgentIds", "callbackResponderAgentId", "deliveryScope"}},
	{Kind: "message_callback_completed", Fields: []string{"effectId", "activationId", "nodeId", "agentId", "port"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "approval_awaiting", Fields: []string{"effectId", "activationId", "nodeId", "kind", "notificationId", "assigneeMemberId"}},
	{Kind: "approval_decided", Fields: []string{"effectId", "activationId", "nodeId", "notificationId", "decision", "decidedByMemberId"}},
	{Kind: "effect_failed", Fields: []string{"effectId", "activationId", "nodeId", "kind"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "effect_retry_scheduled", Fields: []string{"effectId", "activationId", "nodeId", "kind", "retryAt", "messageId", "receiptRecovery"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
	{Kind: "effect_timed_out", Fields: []string{"effectId", "activationId", "nodeId", "kind"}, FailureCapable: true, FailureFields: FailureEventFieldSet},
}

// Lookup returns the producer-owned declaration for an event kind, or nil.
func Lookup(kind string) *ProducerEventFieldSet {
	for index := range ProducerEventFieldSets {
		if ProducerEventFieldSets[index].Kind == kind {
			entry := ProducerEventFieldSets[index]
			return &entry
		}
	}
	return nil
}

// AllKinds returns every declared producer event kind (sorted is not required;
// the caller may reorder).
func AllKinds() []string {
	kinds := make([]string, 0, len(ProducerEventFieldSets))
	for _, entry := range ProducerEventFieldSets {
		kinds = append(kinds, entry.Kind)
	}
	return kinds
}

// ValidEventKind validates an event-kind identity byte-for-byte against the
// closed producer-owned kind vocabulary. A persisted/remote kind is either an
// exact declared kind (returned unchanged) or fails closed; it is never
// TrimSpace'd, UTF-8-replacement-converted, or normalized into a different
// accepted kind. This is the shared discriminator validator used by every public
// RunLog/Event projection boundary so a malformed persisted kind cannot reach a
// top-level Kind/Type/Level/Message field. It does not change the registry
// invariant: the producer-owned ProducerEventFieldSets remain the single source
// of declared kinds, and this helper only reads it.
func ValidEventKind(kind string) (string, bool) {
	for _, declared := range ProducerEventFieldSets {
		if declared.Kind == kind {
			return kind, true
		}
	}
	return "", false
}
