import * as StringUtil from './string.ts';

// 不要在Object.prototype中添加函数，否则vue会报错

export function isObject(obj: any): boolean {
    return typeof obj === 'object' && obj && !Array.isArray(obj);
}

export function assignDeep<T>(target: T, ...sources: object[]): T {
    if (!target) {
        return target;
    }
    const processed = new WeakMap();

    const merge = (target: any, source: any) => {
        if (processed.has(source)) {
            return processed.get(source);
        }
        processed.set(source, target);

        for (const key in source) {
            try {
                const targetValue = target[key];
                const sourceValue = source[key];

                if (isObject(targetValue) && isObject(sourceValue)) {
                    merge(targetValue, sourceValue);
                } else {
                    target[key] = sourceValue;
                }
            } catch (e) {
            }
        }
        return target;
    };

    for (const source of sources) {
        if (source) {
            merge(target, source);
        }
    }
    return target;
}

export type Entry = {
    key: string;
    value: any;
}

export function toKeyValueArray(obj: object, fnValue?: (value: any) => any): Entry[] {
    if (obj) {
        let array = [];
        Object.keys(obj).forEach(key => {
            let value = (obj as any)[key];
            if (typeof fnValue === 'function') {
                value = fnValue(value);
            }
            array.push({
                key: key,
                value: value,
            });
        });
        return array;
    }
    return [];
}

export function setValue(obj: object, fieldPath: string, value: any): any {
    let names = fieldPath.split('.');
    for (let i = 0; i < names.length - 1; i++) {
        let name = names[i];
        obj[name] = obj[name] || {};
        obj = obj[name];
    }
    let lastName = names[names.length - 1];
    obj[lastName] = value;
    return obj;
}

export function deepClone<T>(obj: T): T {
    if (obj) {
        let json = StringUtil.toJson(obj);
        return StringUtil.parseJson(json);
    }
    return obj;
}

export function clear(obj: object, ...excludedKeys: string[]): void {
    if (obj) {
        let keys = Object.keys(obj);
        for (let key of keys) {
            if (!excludedKeys.includes(key)) {
                delete obj[key];
            }
        }
    }
}

export function isNull(value: any): boolean {
    return value === null || value === undefined;
}

export function isNotNull(value: any): boolean {
    return value !== null && value !== undefined;
}

export function isEmpty(value: any): boolean {
    if (isNull(value)) {
        return true;
    }
    if (typeof value === 'string' || Array.isArray(value)) {
        return value.length === 0;
    }
    if (typeof value === 'object' && value) {
        return Object.keys(value).length === 0;
    }
    return false;
}

export function isNotEmpty(value: any): boolean {
    if (isNull(value)) {
        return false;
    }
    if (typeof value === 'string' || Array.isArray(value)) {
        return value.length > 0;
    }
    return true;
}

export function isSimple(value: any): boolean {
    return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'
        || value instanceof Date;
}

export function rewriteDefined<T>(target: T, sources: object | object[], mapper?: (key: string, value: any) => any): T {
    if (target && sources) {
        let sourceArray: object[] = Array.isArray(sources) ? sources : [sources];
        let keys = Object.keys(target);
        for (let key of keys) {
            for (let i = sourceArray.length - 1; i >= 0; i--) {
                let source = sourceArray[i];
                if (typeof source === 'object' && source) {
                    let newValue = source[key];
                    if (typeof mapper === 'function') {
                        newValue = mapper(key, newValue);
                    }
                    if (newValue !== undefined) {
                        let oldValue = target[key];
                        if (typeof oldValue === 'boolean') {
                            newValue = newValue === true || newValue === 'true';
                        } else if (typeof oldValue === 'number' && typeof newValue !== 'number') {
                            newValue = Number(newValue);
                        } else if (typeof oldValue === 'string') {
                            newValue = newValue ? newValue.toString() : '';
                        }
                        target[key] = newValue;
                        break;
                    }
                }
            }
        }
    }
    return target;
}

export function rewriteSimpleDefined<T>(target: T, sources: object | object[], ignoredKeys: string | string[]): T {
    return rewriteDefined(target, sources, (key: string, value: any) => {
        if (!isSimple(value)) {
            return undefined;
        }
        if (ignoredKeys) {
            if (Array.isArray(ignoredKeys)) {
                if (ignoredKeys.includes(key)) {
                    return undefined;
                }
            } else {
                if (ignoredKeys === key) {
                    return undefined;
                }
            }
        }
        return value;
    });
}
