import { checkValueIsEmpty, isNil, isNotNil, isTrue, $any } from '@angular-ru/cdk/utils';

function replaceUndefinedOrNull(_, value) {
    return checkValueIsEmpty(value) ? undefined : value;
}

function clean(object) {
    return JSON.parse(JSON.stringify(object, replaceUndefinedOrNull));
}

function checkIsShallowEmpty(definition) {
    return Object.keys(clean(definition ?? {})).length === 0;
}

function deepClone(value) {
    return isNil(value) ? value : JSON.parse(JSON.stringify(value));
}

// eslint-disable-next-line max-lines-per-function,complexity
const deepFreeze = (value) => {
    const isObject = typeof value === 'object' && value !== null;
    const isDate = value instanceof Date;
    const skipFreeze = !isObject || isDate;
    if (skipFreeze) {
        return value;
    }
    Object.freeze(value);
    const oIsFunction = typeof value === 'function';
    // eslint-disable-next-line @typescript-eslint/unbound-method
    const hasOwnProp = Object.prototype.hasOwnProperty;
    // eslint-disable-next-line complexity
    for (const prop of Object.getOwnPropertyNames(value)) {
        if (hasOwnProp.call(value, prop) &&
            (oIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true) &&
            value[prop] !== null &&
            (typeof value[prop] === 'object' || typeof value[prop] === 'function') &&
            !Object.isFrozen(value[prop])) {
            deepFreeze(value[prop]);
        }
    }
    return value;
};

function firstKey(object) {
    return Object.keys(object ?? {})[0] ?? null;
}

// eslint-disable-next-line complexity
function getValueByPath(object, path, fallback = undefined) {
    if ((path?.length ?? 0) < 1) {
        return object;
    }
    let result = object;
    const parts = path?.split('.') ?? [];
    let index = 0;
    for (; isNotNil(result) && index < parts.length; ++index) {
        const localIndex = parts?.[index];
        result = result?.[localIndex];
    }
    return result ?? fallback;
}

function instanceOfPlainObject(plainObject) {
    return Object.prototype.toString.call(plainObject) === '[object Object]';
}

function isSimpleObject(value) {
    return isNotNil(value) && typeof value == 'object' && instanceOfPlainObject(value);
}

function isGetter(object, prop) {
    let currentObject = object;
    let result = false;
    if (isSimpleObject(currentObject)) {
        while (currentObject !== null) {
            if (isTrue(currentObject?.hasOwnProperty(prop))) {
                // eslint-disable-next-line @typescript-eslint/unbound-method
                result = Boolean(Object.getOwnPropertyDescriptor(currentObject, prop)?.get);
                break;
            }
            else {
                currentObject = Object.getPrototypeOf(currentObject);
            }
        }
    }
    return result;
}

function isIterable(value) {
    return isNotNil(value) && typeof value[Symbol.iterator] === 'function';
}

function isObject(object) {
    return object === Object(object);
}

function isPlainObject(plainObject) {
    let constructorRef;
    let prototypeRef;
    if (!instanceOfPlainObject(plainObject)) {
        return false;
    }
    // If has modified constructor
    // eslint-disable-next-line prefer-const
    constructorRef = plainObject?.constructor;
    if (constructorRef === undefined) {
        return true;
    }
    // If has modified prototype
    // eslint-disable-next-line prefer-const
    prototypeRef = constructorRef.prototype;
    if (!instanceOfPlainObject(prototypeRef)) {
        return false;
    }
    // If constructor does not have an Object-specific method
    if (!prototypeRef.hasOwnProperty('isPrototypeOf')) {
        return false;
    }
    // Most likely a plain Object
    return true;
}

// eslint-disable-next-line complexity
function pathsOfObject(value, parentKey = null, keys = []) {
    for (const key in value) {
        if (!value.hasOwnProperty(key)) {
            continue;
        }
        const element = value[key];
        const isObjectValue = typeof element === 'object' && element !== null && !Array.isArray(element);
        // note: don't use isString for preserve circular dependencies
        const implicitKey = typeof parentKey === 'string' ? `${parentKey}.${key}` : key;
        if (isObjectValue) {
            pathsOfObject(value[key], implicitKey, keys);
        }
        else {
            keys.push(implicitKey);
        }
    }
    return keys;
}

function replaceWithNull(value) {
    return JSON.parse(JSON.stringify(value, (_, element) => {
        if (typeof element === 'object') {
            return element;
        }
        return checkValueIsEmpty(element) ? null : element;
    }));
}

function shallowMapObject(object, mapper) {
    const result = {};
    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            result[key] = mapper(object[key], key);
        }
    }
    return result;
}

function sortByAsc(key, a, b) {
    if ($any(a?.[key]) > $any(b?.[key])) {
        return 1;
    }
    else {
        return $any(a?.[key]) < $any(b?.[key]) ? -1 : 0;
    }
}

function sortByDesc(key, a, b) {
    if ($any(a?.[key]) > $any(b?.[key])) {
        return -1;
    }
    else {
        return $any(a?.[key]) < $any(b?.[key]) ? 1 : 0;
    }
}

function unwrap(target, options) {
    return Object.keys(target)
        .sort()
        .reduce((accumulator, key) => deepObjectReduce({
        key,
        accumulator,
        targetValue: target?.[key],
        options
    }), {});
}
function flatten(object, excludeKeys = []) {
    const depthGraph = {};
    for (const key in object) {
        if (object.hasOwnProperty(key) && !excludeKeys.includes(key)) {
            mutate(object, depthGraph, key);
        }
    }
    return depthGraph;
}
function mutate(object, depthGraph, key) {
    const isObjectLike = typeof object[key] === 'object' && object[key] !== null;
    if (isObjectLike) {
        const flatObject = flatten(object[key]);
        for (const path in flatObject) {
            if (flatObject.hasOwnProperty(path)) {
                depthGraph[`${key}.${path}`] = flatObject[path];
            }
        }
    }
    else {
        depthGraph[key] = object[key];
    }
}
function deepObjectReduce({ accumulator, key, targetValue, options }) {
    let value = targetValue;
    if (isTrue(options.weekType)) {
        const isComplexType = typeof targetValue === 'object' && targetValue !== null;
        value = isComplexType ? targetValue : String(checkValueIsEmpty(targetValue) ? null : targetValue);
    }
    accumulator[key] = getComparableValue(options, value);
    return accumulator;
}
function comparable(target, options = {}) {
    let result = target;
    if (!isObject(result)) {
        return result;
    }
    if (Array.isArray(target)) {
        result = target.map((value) => getComparableValue(options, value));
    }
    else {
        result = unwrap(target, options);
    }
    return result;
}
function objectToString(object, options = {}) {
    return JSON.stringify(comparable(object, options));
}
function equals(a, b, options = {}) {
    return objectToString(a, options) === objectToString(b, options);
}
function shallowTrimProperties(object) {
    return Object.entries(object).reduce((result, [key, value]) => {
        // note: don't use isString for preserve circular dependencies
        result[key] = typeof value === 'string' ? value.trim() : value;
        return result;
    }, {});
}
function strictEquals(a, b) {
    const options = { weekType: true, deep: true };
    return objectToString(a, options) === objectToString(b, options);
}
function getComparableValue(options, value) {
    return isTrue(options.deep) ? comparable(value, options) : value;
}

/**
 * Generated bundle index. Do not edit.
 */

export { checkIsShallowEmpty, clean, comparable, deepClone, deepFreeze, deepObjectReduce, equals, firstKey, flatten, getComparableValue, getValueByPath, isGetter, isIterable, isObject, isPlainObject, isSimpleObject, mutate, objectToString, pathsOfObject, replaceWithNull, shallowMapObject, shallowTrimProperties, sortByAsc, sortByDesc, strictEquals, unwrap };
//# sourceMappingURL=angular-ru-cdk-object.mjs.map
