// The lazy per-value equality index behind NodesWithProperty and friends:
// (label, key) -> value -> node set, built on first access by scanning the
// column, with builds running outside the lock (a racing duplicate is
// discarded -- both are identical).

package chickpeas

import (
	
	
)

type propIndexKey struct {
	label Label
	key   PropertyKey
}

// anyLabel is the reserved sentinel scoping the label-free index built by
// NodeWithProperty.
const anyLabel = Label(^uint32(0))

// valueFromAny resolves a convenience value (string, int, int32, int64,
// float64, bool, or Value) to a Value. A string that was never interned
// resolves to nothing -- no property can equal it.
func ( *Snapshot) ( any) (Value, bool) {
	switch v := .(type) {
	case Value:
		return , true
	case string:
		return .ValueFromString()
	case int64:
		return I64Value(), true
	case int:
		return I64Value(int64()), true
	case int32:
		return I64Value(int64()), true
	case float64:
		return F64Value(), true
	case bool:
		return BoolValue(), true
	}
	return Value{}, false
}

// buildPropValueIndex groups a column's entries by value, restricted to
// labelNodes when non-nil.
func buildPropValueIndex( Column,  *nodeset.Set) map[Value]*nodeset.Set {
	 := map[Value][]uint32{}
	for ,  := range .Entries() {
		if  == nil || .Contains() {
			[] = append([], )
		}
	}
	 := make(map[Value]*nodeset.Set, len())
	for ,  := range  {
		 := roaring.New()
		.AddMany()
		[] = nodeset.FromBitmap()
	}
	return 
}

// withPropValueIndex runs query against the cached per-value index for
// indexKey, building it once via build on a miss. The build runs OUTSIDE
// the lock; build returning nil (unknown column/label) short-circuits.
func ( *Snapshot) (
	 propIndexKey,
	 func() map[Value]*nodeset.Set,
	 func(map[Value]*nodeset.Set) (*nodeset.Set, bool),
) (*nodeset.Set, bool) {
	.propIndexMu.Lock()
	,  := .propIndex[]
	.propIndexMu.Unlock()
	if  {
		return ()
	}
	 := ()
	if  == nil {
		return nil, false
	}
	.propIndexMu.Lock()
	if ,  := .propIndex[];  {
		 = 
	} else {
		.propIndex[] = 
	}
	.propIndexMu.Unlock()
	return ()
}

// NodesWithValue is the set of label-carrying nodes whose property key
// equals v -- the typed core of NodesWithProperty. The index for (label,
// key) is built lazily on first access and cached. The returned set is
// shared -- callers must not mutate it (Clone first). ok is false when the
// label/key is unknown or no node carries that value.
func ( *Snapshot) (,  string,  Value) (*nodeset.Set, bool) {
	,  := .Label()
	if ! {
		return nil, false
	}
	,  := .PropertyKey()
	if ! {
		return nil, false
	}
	return .withPropValueIndex(
		propIndexKey{label: , key: },
		func() map[Value]*nodeset.Set {
			,  := .labelIndex[]
			if ! {
				return nil
			}
			,  := .columns[]
			if ! {
				return nil
			}
			return buildPropValueIndex(, )
		},
		func( map[Value]*nodeset.Set) (*nodeset.Set, bool) {
			,  := []
			return , 
		},
	)
}

// NodesWithProperty is NodesWithValue with a convenience value (string,
// int, int32, int64, float64, bool, or Value).
func ( *Snapshot) (,  string,  any) (*nodeset.Set, bool) {
	,  := .valueFromAny()
	if ! {
		return nil, false
	}
	return .NodesWithValue(, , )
}

// NodeWithProperty finds a single node by a property value across ALL
// labels (unlike the label-scoped NodesWithProperty) -- for unique keys
// such as a uri. Returns the smallest matching node id. The label-free
// (key, value) index is built lazily and cached under a reserved sentinel.
func ( *Snapshot) ( string,  any) (NodeID, bool) {
	,  := .PropertyKey()
	if ! {
		return 0, false
	}
	,  := .valueFromAny()
	if ! {
		return 0, false
	}
	,  := .withPropValueIndex(
		propIndexKey{label: anyLabel, key: },
		func() map[Value]*nodeset.Set {
			,  := .columns[]
			if ! {
				return nil
			}
			return buildPropValueIndex(, nil)
		},
		func( map[Value]*nodeset.Set) (*nodeset.Set, bool) {
			,  := []
			return , 
		},
	)
	if ! {
		return 0, false
	}
	return .Min()
}

// NodeWithLabelProperty finds a single label-carrying node whose property
// key equals value -- the label-scoped sibling of NodeWithProperty, for
// keys unique only within a label. Returns the smallest matching node id;
// reuses the cached (label, key) index NodesWithProperty builds.
func ( *Snapshot) (,  string,  any) (NodeID, bool) {
	,  := .NodesWithProperty(, , )
	if ! {
		return 0, false
	}
	return .Min()
}