// Builder pre-finalization read probes: staged counts, property and label
// reads, value-scan and neighbor projections. All are O(pairs)/O(rels)
// scans over the staging state, never interning (reads must not grow the
// atom table).

package chickpeas

// NodeCount is the number of distinct nodes staged so far.
func ( *Builder) () int {
	return int(.knownNodes.GetCardinality())
}

// RelCount is the number of live relationships staged so far (tombstoned
// rels and pending detach-delete cascades excluded).
func ( *Builder) () int {
	if !.hasRemovals() {
		return len(.rels)
	}
	 := 0
	for  := range .rels {
		if !.relRemoved() {
			++
		}
	}
	return 
}

// Prop reads node's staged value for key -- an O(pairs) pre-finalization
// probe that never interns (a string value never interned can't be staged
// anywhere). Returns the FIRST staged write, matching the Rust builder.
func ( *Builder) ( NodeID,  string) (Value, bool) {
	,  := .interner.Get()
	if ! {
		return Value{}, false
	}
	for ,  := range .nodeColStr[] {
		if .id ==  {
			return StrValue(.val), true
		}
	}
	for ,  := range .nodeColI64[] {
		if .id ==  {
			return I64Value(.val), true
		}
	}
	for ,  := range .nodeColF64[] {
		if .id ==  {
			return F64Value(.val), true
		}
	}
	for ,  := range .nodeColBool[] {
		if .id ==  {
			return BoolValue(.val), true
		}
	}
	return Value{}, false
}

// ResolveString resolves an interner atom back to its string (for reading
// staged Prop values); ok is false when out of range.
func ( *Builder) ( uint32) (string, bool) {
	return .interner.Resolve()
}

// NodesWithProperty lists the label-carrying nodes whose staged property
// key equals value, in staging order -- an O(pairs) pre-finalization scan.
// A string value never interned in this builder can't be on any node, so
// the probe returns empty WITHOUT interning it (reads must not grow the
// atom table).
func ( *Builder) (,  string,  any) []NodeID {
	,  := .interner.Get()
	if ! {
		return nil
	}
	,  := .interner.Get()
	if ! {
		return nil
	}
	var  Value
	switch v := .(type) {
	case Value:
		 = 
	case string:
		,  := .interner.Get()
		if ! {
			return nil
		}
		 = StrValue()
	default:
		,  := .stageValue() // numeric/bool: never interns
		if  != nil {
			return nil
		}
		 = 
	}
	 := func( NodeID) bool {
		if int() >= len(.nodeLabels) {
			return false
		}
		for ,  := range .nodeLabels[] {
			if .ID() ==  {
				return true
			}
		}
		return false
	}
	var  []NodeID
	// Scan only the column whose type matches the probe value.
	switch .Kind() {
	case KindI64:
		,  := .I64()
		for ,  := range .nodeColI64[] {
			if .val ==  && (.id) {
				 = append(, .id)
			}
		}
	case KindF64:
		for ,  := range .nodeColF64[] {
			if F64Value(.val) ==  && (.id) {
				 = append(, .id)
			}
		}
	case KindBool:
		,  := .Bool()
		for ,  := range .nodeColBool[] {
			if .val ==  && (.id) {
				 = append(, .id)
			}
		}
	case KindStr:
		,  := .StrID()
		for ,  := range .nodeColStr[] {
			if .val ==  && (.id) {
				 = append(, .id)
			}
		}
	}
	return 
}

// NodeLabels lists node's staged labels in insertion order.
func ( *Builder) ( NodeID) []string {
	if int() >= len(.nodeLabels) {
		return nil
	}
	 := make([]string, 0, len(.nodeLabels[]))
	for ,  := range .nodeLabels[] {
		if ,  := .interner.Resolve(.ID());  {
			 = append(, )
		}
	}
	return 
}

// NeighborIDs lists node's staged neighbors in direction (outgoing then
// incoming for Both), skipping removed rels -- an O(rels) pre-finalization
// scan.
func ( *Builder) ( NodeID,  Direction) []NodeID {
	var  []NodeID
	if  == Outgoing ||  == Both {
		for ,  := range .rels {
			if [0] ==  && !.relRemoved() {
				 = append(, [1])
			}
		}
	}
	if  == Incoming ||  == Both {
		for ,  := range .rels {
			if [1] ==  && !.relRemoved() {
				 = append(, [0])
			}
		}
	}
	return 
}