Source: utils/keyCompare.js

// @ts-check

/**
 * Key Comparison Utilities
 * 
 * Provides functions for comparing objects and detecting changes, with special handling
 * for encrypted fields like accounts.
 * 
 * @module utils/keyCompare
 */

import { accountsEqual } from "./crypto.js"

import { filterKeys } from './objectfilter/filters/filterPerson.js'


/**
 * Compares two objects and returns the differences
 * 
 * Compares all keys in newData with oldData and identifies:
 * - Modified values (different in newData vs oldData)
 * - Deleted keys (present in oldData but undefined in newData)
 * 
 * @param {Object} oldData - Original object data
 * @param {Object} newData - New object data to compare against
 * @returns {Object} Object containing only the changed or deleted keys
 */
export const keysCompare = (oldData, newData) => {
    // takes all new keys and checks, if they are the same in oldData
    const diff = {}
    Object.entries(newData).forEach(([key, value]) => {
        if (!oldData || !oldData[key] || JSON.stringify(value) !== JSON.stringify(oldData[key]))
            diff[key] = value
    })


    // now add all deleted keys in oldData with value null
    if (oldData) {
        Object.keys(oldData).forEach(key => {
            if (newData[key] === undefined)
                diff[key] = undefined
        })
    }
    return diff
}

/**
 * Compares two objects for equality and returns detailed change information
 * 
 * Performs deep comparison with special handling for encrypted accounts field.
 * Returns a tuple containing:
 * 1. Boolean indicating if objects are equal
 * 2. Object containing all differences
 * 3. Object containing only filter-relevant differences (or null if none)
 * 
 * Special handling:
 * - Accounts are compared using accountsEqual (decrypted comparison)
 * - Only changes to filterKeys properties are included in filterDiff
 * 
 * @param {Object} oldData - Original object data
 * @param {Object} newData - New object data to compare against
 * @returns {[boolean, Object, Object|null]} Tuple: [isEqual, allDifferences, filterRelevantDifferences]
 */
export const keysEqual = (oldData, newData) => {
    // accounts are encryped and may look different, but are equal
    // we compare the data deeply and return an array with the boolean equality and the diff
    const myDiff = keysCompare(oldData, newData)
    let filterDiff = {...myDiff}
    const myDiffLength = Object.keys(myDiff).length
    if (myDiffLength === 0) {
        return [true, {}, null]
    }
    else if (myDiff.accounts) {
        if (accountsEqual(oldData.accounts, newData.accounts)) {
            delete myDiff.accounts
            delete filterDiff.accounts
        }
    }
    // only trigger list checks for changes which are used in object filters
    // so delete in filterDiff the keys, which are not in filterKeys
    Object.keys(myDiff).forEach(key => {
        if (!Object.keys(filterKeys).includes(key))
            delete filterDiff[key]
    })
    
    // If no filter-relevant changes remain, set filterDiff to null
    if (Object.keys(filterDiff).length === 0) {
        filterDiff = null
    }
    
    return [false, myDiff, filterDiff]
}