/**
 * Property comparison functions for visible-state diffing.
 *
 * Covers node property maps, edge property maps, and per-node-view property deltas.
 * Pure functions — no side effects, no I/O, no classes.
 *
 * @module domain/services/comparison/diffProperties
 */
import { valueKey, compareNodePropRefs, compareEdgePropRefs, compareStrings, } from "./diffKeys.js";
/**
 * Finds removed and changed entries by iterating the left map against the right.
 */
export function findNodePropRemovedAndChanged(left, right) {
    const removed = [];
    const changed = [];
    for (const [key, leftEntry] of left.entries()) {
        const rightEntry = right.get(key);
        if (rightEntry === undefined) {
            removed.push(leftEntry);
            continue;
        }
        if (valueKey(leftEntry.value) !== valueKey(rightEntry.value)) {
            changed.push({
                node: leftEntry.node,
                key: leftEntry.key,
                leftValue: leftEntry.value,
                rightValue: rightEntry.value,
            });
        }
    }
    return { removed, changed };
}
/**
 * Finds entries present in right but absent from left.
 */
export function findNodePropAdded(left, right) {
    const added = [];
    for (const [key, rightEntry] of right.entries()) {
        if (!left.has(key)) {
            added.push(rightEntry);
        }
    }
    return added;
}
/**
 * Computes added, removed, and changed deltas between two node property maps.
 */
export function compareNodePropertyMaps(left, right) {
    const { removed, changed } = findNodePropRemovedAndChanged(left, right);
    const added = findNodePropAdded(left, right);
    added.sort(compareNodePropRefs);
    removed.sort(compareNodePropRefs);
    changed.sort(compareNodePropRefs);
    return { added, removed, changed };
}
/**
 * Finds removed and changed entries by iterating the left edge property map against the right.
 */
export function findEdgePropRemovedAndChanged(left, right) {
    const removed = [];
    const changed = [];
    for (const [key, leftEntry] of left.entries()) {
        const rightEntry = right.get(key);
        if (rightEntry === undefined) {
            removed.push(leftEntry);
            continue;
        }
        if (valueKey(leftEntry.value) !== valueKey(rightEntry.value)) {
            changed.push({
                from: leftEntry.from,
                to: leftEntry.to,
                label: leftEntry.label,
                key: leftEntry.key,
                leftValue: leftEntry.value,
                rightValue: rightEntry.value,
            });
        }
    }
    return { removed, changed };
}
/**
 * Finds edge property entries present in right but absent from left.
 */
export function findEdgePropAdded(left, right) {
    const added = [];
    for (const [key, rightEntry] of right.entries()) {
        if (!left.has(key)) {
            added.push(rightEntry);
        }
    }
    return added;
}
/**
 * Computes added, removed, and changed deltas between two edge property maps.
 */
export function compareEdgePropertyMaps(left, right) {
    const { removed, changed } = findEdgePropRemovedAndChanged(left, right);
    const added = findEdgePropAdded(left, right);
    added.sort(compareEdgePropRefs);
    removed.sort(compareEdgePropRefs);
    changed.sort(compareEdgePropRefs);
    return { added, removed, changed };
}
/**
 * Computes the ownership status of a key in left and right property records.
 */
export function keyOwnership(key, left, // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
right) {
    return {
        leftHas: Object.prototype.hasOwnProperty.call(left, key),
        rightHas: Object.prototype.hasOwnProperty.call(right, key),
    };
}
/**
 * Classifies a property key that exists in both sides as changed or unchanged.
 */
export function classifySharedPropertyKey(key, props, // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
changed) {
    if (valueKey(props.left[key]) !== valueKey(props.right[key])) {
        changed.push({ key, leftValue: props.left[key], rightValue: props.right[key] });
    }
}
/**
 * Classifies a single property key into added, removed, or changed.
 */
export function classifyPropertyKey(key, props, // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
delta) {
    const { leftHas, rightHas } = keyOwnership(key, props.left, props.right);
    if (leftHas && !rightHas) {
        delta.removed.push({ key, value: props.left[key] });
        return;
    }
    if (!leftHas && rightHas) {
        delta.added.push({ key, value: props.right[key] });
        return;
    }
    classifySharedPropertyKey(key, props, delta.changed);
}
/**
 * Compares property records from two node views and returns the delta.
 */
export function compareNodeViewProperties(leftProps, // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
rightProps) {
    const propertyKeys = [
        ...new Set([...Object.keys(leftProps), ...Object.keys(rightProps)]),
    ].sort(compareStrings);
    const propertyDelta = { added: [], removed: [], changed: [] };
    const props = { left: leftProps, right: rightProps };
    for (const key of propertyKeys) {
        classifyPropertyKey(key, props, propertyDelta);
    }
    return propertyDelta;
}
