// For nested objects, spread operator only creates new references to the top level fields,
// the remaining nested fields actually reference the original object which can introduce problems.

// The purpose of this method is to deep clone the data in an object recursively, this is useful
// for cloning modelValues to internal models when regular two-way binding is not an option.
// See: mobile-location-modal-questions.vue

// Creates a deep clone of an object. Clones primitives, arrays and objects, excluding class instances.
// https://www.30secondsofcode.org/js/s/deep-clone
export function deepClone(object) {
    if (object === null) {
        return null;
    }

    const clone = { ...object };
    // eslint-disable-next-line no-return-assign
    Object.keys(clone).forEach((key) =>
        (clone[key] = typeof object[key] === 'object' ? deepClone(object[key]) : object[key]));

    if (Array.isArray(object)) {
        clone.length = object.length;
        return Array.from(clone);
    }

    return clone;
}

// The purpose of this method is to check for array or object equality recursively to determine if two complex objects are equal.
// This is only a comparison of data, not functions.
export function deepEqual(obj1, obj2) {
    if (typeof obj1 !== typeof obj2) {
        return false;
    }

    if (obj1 === null || obj2 === null) {
        return obj1 === obj2;
    }

    if (Array.isArray(obj1) && Array.isArray(obj2)) {
        if (obj1.length !== obj2.length) {
            return false;
        }

        const sorted1 = obj1.slice().sort();
        const sorted2 = obj2.slice().sort();

        for (let i = 0; i < sorted1.length; i++) {
            if (!deepEqual(sorted1[i], sorted2[i])) {
                return false;
            }
        }

        return true;
    }

    if (typeof obj1 === 'object' && typeof obj2 === 'object') {
        const keys1 = Object.keys(obj1);
        const keys2 = Object.keys(obj2);

        if (keys1.length !== keys2.length) {
            return false;
        }

        const sortedKeys1 = keys1.sort();
        const sortedKeys2 = keys2.sort();

        for (let i = 0; i < sortedKeys1.length; i++) {
            const key1 = sortedKeys1[i];
            const key2 = sortedKeys2[i];

            if (key1 !== key2 || !deepEqual(obj1[key1], obj2[key2])) {
                return false;
            }
        }

        return true;
    }

    return obj1 === obj2;
}

export function getPropertyCaseInsensitive(obj, property) {
    const props = [];
    for (const i in obj) if (Object.prototype.hasOwnProperty.call(obj, i)) props.push(i);
    let prop;
    // eslint-disable-next-line no-cond-assign
    while (prop = props.pop()) if (prop.toLowerCase() === property.toLowerCase()) return prop;
    return null;
}
