/*
 * Copyright 2017-2025 VMware, Inc. All rights reserved. VMware Confidential
 */
/**
 * Helper methods for JSON objects.
 *
 * A JSON object is defined as any object that could be created by de-serializing JSON.
 */
export const JsonObjectHelper = {
    /**
     * Clones a JSON object.
     * @param obj
     * @return {T} clone.
     */
    clone: function <T>(obj: T): T {
        return <T>JSON.parse(JSON.stringify(obj));
    },

    /**
     * Helper method for deep equality of two JSON objects.
     * Objects do not have to have keys in the same order.
     * @param a JSON object
     * @param b JSON object
     * @return equality
     */
    deepEquals: function <T>(a: T, b: T): boolean {
        if (a !== b) {
            // Identity check
            const aType = typeof a;

            if (
                (a === null && b !== null) ||
                (b === null && a !== null) ||
                (a === undefined && b !== undefined) ||
                (b === undefined && a !== undefined)
            ) {
                // Null mismatch (typeof null == 'object')
                return false;
            } else if (Array.isArray(a)) {
                // Array comparison
                if (!Array.isArray(b)) {
                    // Other side not an array
                    return false;
                }

                if (a.length != b.length) {
                    return false;
                }

                for (let i = 0; i < a.length; i++) {
                    if (!JsonObjectHelper.deepEquals(a[i], b[i])) {
                        // Recurse on array values
                        return false;
                    }
                }
            } else if (aType == "object") {
                // Object comparison
                const aKeys = {};

                if (Array.isArray(b)) {
                    // Other side an array
                    return false;
                }

                for (const aKey of Object.keys(a)) {
                    aKeys[aKey] = true;
                }

                for (const bKey of Object.keys(b)) {
                    if (aKeys[bKey] === undefined) {
                        // Key in b not on a
                        return false;
                    }

                    if (!JsonObjectHelper.deepEquals(a[bKey], b[bKey])) {
                        // Recurse on key values
                        return false;
                    }

                    aKeys[bKey] = false;
                }

                for (const aKey in aKeys) {
                    if (aKeys[aKey]) {
                        // Key in a not on b
                        return false;
                    }
                }
            } else {
                // This can't be checked.  These aren't identical (===) so return false;
                return false;
            }
        }

        return true; // All other failure cases exhausted, must be equal
    },
};
