import isEmpty from './isEmpty';
import debounce from './debounce';

/**
 * bind func to obj
 */
function bindFunc(obj, method, observer) {
    const oldFn = obj[method];
    obj[method] = function (target) {
        if (observer) {
            observer.call(this, target, {
                [method]: true
            });
        }
        if (oldFn) {
            oldFn.call(this, target);
        }
    };
}

// default methods
const methods = ['linked', 'linkChanged', 'unlinked'];

// extra props
const extProps = ['observer'];
export default {
    data() {
        return {};
    },
    lifetimes: {
        created() {
            this._debounce = null;
        },
        detached() {
            if (this._debounce && this._debounce.cancel) {
                this._debounce.cancel();
            }
        }
    },
    definitionFilter(defFields) {
        const { relations } = defFields;
        if (!isEmpty(relations)) {
            for (const key in relations) {
                const relation = relations[key];

                // bind func
                methods.forEach((method) => bindFunc(relation, method, relation.observer));

                // delete extProps
                extProps.forEach((prop) => delete relation[prop]);
            }
        }
        Object.assign((defFields.methods = defFields.methods || {}), {
            getRelationsName: function (types = ['parent', 'child', 'ancestor', 'descendant']) {
                return Object.keys(relations || {})
                    .map((key) => {
                        if (relations[key] && types.includes(relations[key].type)) {
                            return key;
                        }
                        return null;
                    })
                    .filter((v) => !!v);
            },
            debounce: function (func, wait = 0, immediate = false) {
                return (this._debounce = this._debounce || debounce(func.bind(this), wait, immediate)).call(this);
            }
        });
    }
};
