package triggerprojection

import (
	"bytes"
	"encoding/json"

	"github.com/nexuscrm/shared/pkg/models"
)

// ResolveTriggerOrder extracts the triggerOrder integer from the active
// FlowVersion's metadata. It is the single SSoT helper for this fact —
// both the Tooling FlowDefinition projection and the runtime flow-match sort
// MUST call this function and no other path.
//
// The parser is intentionally lenient:
//   - triggerOrder absent → nil
//   - triggerOrder is a JSON number → *int with that value
//   - triggerOrder is any other JSON type (string, bool, array…) → nil
//
// This matches the pre-existing behaviour of metadataForProjection, which
// treated triggerOrder as an unknown field before this field was introduced:
// malformed non-numeric values are ignored rather than treated as errors, so
// corrupt triggerOrder data in persisted metadata never breaks flow triggering.
//
// Returns nil when version is nil, when MetadataRaw is absent, when the
// metadata has no start element, or when triggerOrder is absent or non-numeric.
func ResolveTriggerOrder(version *models.FlowVersion) *int {
	if version == nil {
		return nil
	}

	raw := bytes.TrimSpace(version.MetadataRaw)
	if len(raw) == 0 || bytes.Equal(raw, []byte("{}")) {
		return nil
	}

	// Extract start.triggerOrder from MetadataRaw using a lenient partial parse.
	// Decoding triggerOrder as interface{} lets a non-numeric JSON value
	// (string, bool, etc.) silently become nil rather than a hard error.
	var wire struct {
		Start *json.RawMessage `json:"start"`
	}
	if err := json.Unmarshal(raw, &wire); err != nil || wire.Start == nil {
		return nil
	}
	startRaw := bytes.TrimSpace(*wire.Start)
	if len(startRaw) == 0 || bytes.Equal(startRaw, []byte("null")) {
		return nil
	}

	var startOrder struct {
		TriggerOrder interface{} `json:"triggerOrder"`
	}
	if err := json.Unmarshal(startRaw, &startOrder); err != nil {
		return nil
	}

	// JSON numbers unmarshal into float64 when the target type is interface{}.
	if v, ok := startOrder.TriggerOrder.(float64); ok {
		i := int(v)
		return &i
	}
	return nil
}
