import _remove from 'lodash/remove.js';
import _isEqual from 'lodash/isEqual.js';
import _mean from 'lodash/mean.js';
import _sum from 'lodash/sum.js';
import _min from 'lodash/min.js';
import _max from 'lodash/max.js';
import EventEmitter from 'events';
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
import Debug from 'debug';
import { minilinksQuery, minilinksQueryIs } from './minilinks-query.js';
import { useDebounceCallback } from '@react-hook/debounce';
import { Observable } from '@apollo/client/index.js';
const debug = Debug('deeplinks:minilinks');
const log = debug.extend('log');
const error = debug.extend('error');
const nativeLinkKeys = ['id', 'type_id', 'from_id', 'to_id', 'value', 'type', 'typed', 'from', 'out', 'outByType', 'to', 'in', 'inByType', '_applies', '_namespaces', 'ml'];
export function toPlain(link) {
    return {
        id: link._id || link.id,
        type_id: link._type_id || link.type_id,
        from_id: link._from_id || link.from_id,
        to_id: link._to_id || link.to_id,
        value: link.value || undefined,
    };
}
export class MinilinksLink {
    get type_id() {
        var _a;
        return (((_a = this.ml) === null || _a === void 0 ? void 0 : _a.virtualInverted[this._type_id]) || this._type_id);
    }
    get from_id() {
        var _a;
        return (((_a = this.ml) === null || _a === void 0 ? void 0 : _a.virtualInverted[this._from_id]) || this._from_id);
    }
    get to_id() {
        var _a;
        return (((_a = this.ml) === null || _a === void 0 ? void 0 : _a.virtualInverted[this._to_id]) || this._to_id);
    }
    set type_id(id) {
        this._type_id = id;
    }
    set from_id(id) {
        this._from_id = id;
    }
    set to_id(id) {
        this._to_id = id;
    }
    get typed() {
        var _a, _b;
        return ((_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byType) === null || _b === void 0 ? void 0 : _b[this.id]) || [];
    }
    get type() {
        var _a, _b;
        return (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byId) === null || _b === void 0 ? void 0 : _b[this.type_id];
    }
    get in() {
        var _a, _b;
        return ((_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byTo) === null || _b === void 0 ? void 0 : _b[this.id]) || [];
    }
    get inByType() {
        var _a, _b, _c, _d, _e;
        const hash = {};
        for (let i = 0; i < (((_c = (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byTo) === null || _b === void 0 ? void 0 : _b[this.id]) === null || _c === void 0 ? void 0 : _c.length) || 0); i++) {
            const element = (_e = (_d = this.ml) === null || _d === void 0 ? void 0 : _d.byTo) === null || _e === void 0 ? void 0 : _e[this.id][i];
            hash[element.type_id] = hash[element.type_id] || [];
            hash[element.type_id].push(element);
        }
        return hash;
    }
    get out() {
        var _a, _b;
        return (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byFrom) === null || _b === void 0 ? void 0 : _b[this.id];
    }
    get outByType() {
        var _a, _b, _c, _d, _e, _f;
        const hash = {};
        for (let i = 0; i < (((_c = (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byFrom) === null || _b === void 0 ? void 0 : _b[this.id]) === null || _c === void 0 ? void 0 : _c.length) || 0); i++) {
            const element = (_f = (_e = (_d = this.ml) === null || _d === void 0 ? void 0 : _d.byFrom) === null || _e === void 0 ? void 0 : _e[this.id]) === null || _f === void 0 ? void 0 : _f[i];
            hash[element.type_id] = hash[element.type_id] || [];
            hash[element.type_id].push(element);
        }
        return hash;
    }
    get from() {
        var _a, _b;
        return (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byId) === null || _b === void 0 ? void 0 : _b[this.from_id];
    }
    get to() {
        var _a, _b;
        return (_b = (_a = this.ml) === null || _a === void 0 ? void 0 : _a.byId) === null || _b === void 0 ? void 0 : _b[this.to_id];
    }
    get displayId() {
        return this._id || this.id;
    }
    get value() {
        return (this === null || this === void 0 ? void 0 : this.string) || (this === null || this === void 0 ? void 0 : this.number) || (this === null || this === void 0 ? void 0 : this.object);
    }
    constructor(link) {
        var _a, _b, _c;
        this._applies = [];
        this._namespaces = [];
        this.ml = link.ml;
        this.id = link.id;
        this._id = link._id;
        this._type_id = link.type_id;
        this._from_id = link.from_id;
        this._to_id = link.to_id;
        this._applies = link._applies;
        if (link.value) {
            if (typeof ((_a = link === null || link === void 0 ? void 0 : link.value) === null || _a === void 0 ? void 0 : _a.value) === 'string' && !this.string)
                this.string = link.value;
            if (typeof ((_b = link === null || link === void 0 ? void 0 : link.value) === null || _b === void 0 ? void 0 : _b.value) === 'number' && !this.number)
                this.number = link.value;
            if (typeof ((_c = link === null || link === void 0 ? void 0 : link.value) === null || _c === void 0 ? void 0 : _c.value) === 'object' && !this.object)
                this.object = link.value;
        }
        const keys = Object.keys(link);
        for (const key of keys) {
            if (!nativeLinkKeys.includes(key))
                this[key] = link[key];
        }
    }
    toPlain() {
        return toPlain(this);
    }
    is(query) {
        return minilinksQueryIs(query, this);
    }
}
export const MinilinksGeneratorOptionsDefault = {
    id: 'id',
    type_id: 'type_id',
    type: 'type',
    typed: 'typed',
    from_id: 'from_id',
    from: 'from',
    out: 'out',
    to_id: 'to_id',
    to: 'to',
    in: 'in',
    inByType: 'inByType',
    outByType: 'outByType',
    equal: (ol, nl) => {
        const _ol = toPlain(ol);
        const _nl = toPlain(nl);
        return _ol.type_id == _nl.type_id && _ol.from_id == _nl.from_id && _ol.to_id == _nl.to_id && _isEqual(_ol.value, _nl.value);
    },
    Link: MinilinksLink,
};
export function Minilinks(options) {
    return function minilinks(linksArray = [], memory = {}) {
        const mc = new MinilinkCollection(options, memory);
        mc.add(linksArray);
        return mc;
    };
}
export class MinilinkCollection {
    query(query, options) {
        const result = minilinksQuery(query, this);
        if ((options === null || options === void 0 ? void 0 : options.aggregate) === 'count')
            return result === null || result === void 0 ? void 0 : result.length;
        if ((options === null || options === void 0 ? void 0 : options.aggregate) === 'avg')
            return _mean(result === null || result === void 0 ? void 0 : result.map(l => { var _a; return (_a = l === null || l === void 0 ? void 0 : l.value) === null || _a === void 0 ? void 0 : _a.value; }));
        if ((options === null || options === void 0 ? void 0 : options.aggregate) === 'sum')
            return _sum(result === null || result === void 0 ? void 0 : result.map(l => { var _a; return (_a = l === null || l === void 0 ? void 0 : l.value) === null || _a === void 0 ? void 0 : _a.value; }));
        if ((options === null || options === void 0 ? void 0 : options.aggregate) === 'min')
            return _min(result === null || result === void 0 ? void 0 : result.map(l => { var _a; return (_a = l === null || l === void 0 ? void 0 : l.value) === null || _a === void 0 ? void 0 : _a.value; }));
        if ((options === null || options === void 0 ? void 0 : options.aggregate) === 'max')
            return _max(result === null || result === void 0 ? void 0 : result.map(l => { var _a; return (_a = l === null || l === void 0 ? void 0 : l.value) === null || _a === void 0 ? void 0 : _a.value; }));
        return result;
    }
    select(query, options) {
        return this.query(query, options);
    }
    subscribe(query) {
        const ml = this;
        return new Observable((observer) => {
            let prev = ml.query(query);
            observer.next(prev);
            let listener = (oldL, newL) => {
                const data = ml.query(query);
                if (!_isEqual(prev, data)) {
                    prev = data;
                    observer.next(data);
                }
            };
            ml.emitter.on('added', listener);
            ml.emitter.on('updated', listener);
            ml.emitter.on('removed', listener);
            return () => {
                ml.emitter.removeListener('added', listener);
                ml.emitter.removeListener('updated', listener);
                ml.emitter.removeListener('removed', listener);
            };
        });
    }
    add(linksArray, applyName = '') {
        log('add', linksArray, this);
        const { byId, byFrom, byTo, byType, links, options } = this;
        const anomalies = [];
        const errors = [];
        const newLinks = [];
        for (let l = 0; l < linksArray.length; l++) {
            if (!!byId[linksArray[l][options.id]])
                errors.push(new Error(`${linksArray[l][options.id]} can't add because already exists in collection`));
            if (byId[linksArray[l][options.id]]) {
                if (options.handler)
                    options.handler(byId[linksArray[l][options.id]], this);
            }
            else {
                const isVirtual = linksArray[l].id < 0;
                if (isVirtual && !this.virtual.hasOwnProperty(linksArray[l].id)) {
                    this.virtual[linksArray[l].id] = undefined;
                }
                const link = new this.options.Link(Object.assign({ ml: this, _applies: [applyName], _namespaces: [], _id: isVirtual ? undefined : linksArray[l].id }, linksArray[l]));
                byId[link[options.id]] = link;
                if (link[options.from_id]) {
                    if (byFrom[link[options.from_id]])
                        byFrom[link[options.from_id]].push(link);
                    else
                        byFrom[link[options.from_id]] = [link];
                }
                if (link[options.to_id]) {
                    if (byTo[link[options.to_id]])
                        byTo[link[options.to_id]].push(link);
                    else
                        byTo[link[options.to_id]] = [link];
                }
                if (link[options.type_id]) {
                    if (byType[link[options.type_id]])
                        byType[link[options.type_id]].push(link);
                    else
                        byType[link[options.type_id]] = [link];
                }
                links.push(link);
                newLinks.push(link);
            }
        }
        for (let l = 0; l < newLinks.length; l++) {
            const link = newLinks[l];
            const type = byId[link[options.type_id]];
            const from = byId[link[options.from_id]];
            const to = byId[link[options.to_id]];
            if (type) {
            }
            else if (link[options.type_id])
                anomalies.push(new Error(`${link[options.id]} link.type_id ${link[options.type_id]} not found`));
            if (from) {
            }
            else if (link[options.from_id])
                anomalies.push(new Error(`${link[options.id]} link.from_id ${link[options.from_id]} not found`));
            if (to) {
            }
            else if (link[options.to_id])
                anomalies.push(new Error(`${link[options.id]} link.to_id ${link[options.to_id]} not found`));
            if (options.handler)
                options.handler(link, this);
        }
        for (let l = 0; l < newLinks.length; l++) {
            const link = newLinks[l];
            if (!this._updating)
                this.emitter.emit('added', undefined, link);
        }
        return {
            anomalies,
            errors,
        };
    }
    remove(idsArray) {
        log('remove', idsArray, this);
        const { byId, byFrom, byTo, byType, types, links, options } = this;
        const anomalies = [];
        const errors = [];
        const oldLinksArray = [];
        const oldLinksObject = {};
        for (let l = 0; l < idsArray.length; l++) {
            const id = idsArray[l];
            const link = byId[id];
            log('remove old l:', l, 'id:', id, 'link:', link);
            if (link) {
                oldLinksArray.push(link);
                oldLinksObject[id] = link;
            }
        }
        for (let l = 0; l < idsArray.length; l++) {
            const id = idsArray[l];
            const link = oldLinksArray[l];
            if (!link)
                errors.push(new Error(`${id} can't delete because not exists in collection`));
            _remove((byFrom === null || byFrom === void 0 ? void 0 : byFrom[link === null || link === void 0 ? void 0 : link[options.from_id]]) || [], (r) => r.id === id);
            _remove((byTo === null || byTo === void 0 ? void 0 : byTo[link === null || link === void 0 ? void 0 : link[options.to_id]]) || [], (r) => r.id === id);
            _remove((byType === null || byType === void 0 ? void 0 : byType[link === null || link === void 0 ? void 0 : link[options.type_id]]) || [], (r) => r.id === id);
            byId === null || byId === void 0 ? true : delete byId[id];
        }
        _remove(links, l => idsArray.includes(l[options.id]));
        for (let l = 0; l < oldLinksArray.length; l++) {
            const link = oldLinksArray[l];
            log('emit removed link', link, '_updating', this._updating);
            if (!this._updating)
                this.emitter.emit('removed', link);
        }
        return {
            anomalies,
            errors,
        };
    }
    _toPlainLinksArray(linkOrLinks, applyOptions, plainLinksArray, returnLinksPathsById) {
        var _a;
        const links = Array.isArray(linkOrLinks) ? linkOrLinks : [linkOrLinks];
        plainLinksArray.push(...links.map(l => (Object.assign(Object.assign({}, l), { id: l.id, type_id: l.type_id, from_id: l.from_id, to_id: l.to_id, value: l.value }))));
        for (let l in links) {
            for (let r in ((applyOptions === null || applyOptions === void 0 ? void 0 : applyOptions.return) || {})) {
                this._toPlainLinksArray(links[l][r] || [], (_a = applyOptions === null || applyOptions === void 0 ? void 0 : applyOptions.return) === null || _a === void 0 ? void 0 : _a[r], plainLinksArray, returnLinksPathsById);
            }
        }
    }
    apply(_input, applyName = '', applyOptions) {
        const input = Array.isArray(_input) ? _input : Array.isArray(_input === null || _input === void 0 ? void 0 : _input.data) ? _input === null || _input === void 0 ? void 0 : _input.data : [];
        const returning = (_input === null || _input === void 0 ? void 0 : _input.return) || {};
        const _applyOptions = Object.assign({ return: returning }, applyOptions);
        const deep = _input === null || _input === void 0 ? void 0 : _input.deep;
        const namespace = deep === null || deep === void 0 ? void 0 : deep.namespace;
        log('apply', input, this);
        const { byId, byFrom, byTo, byType, types, links, options } = this;
        const toAdd = [];
        const toUpdate = [];
        const beforeUpdate = {};
        const toRemove = [];
        const _byId = {};
        const linksArray = [];
        const returnLinksPathsById = {};
        for (let l = 0; l < input.length; l++) {
            const link = input[l];
            this._toPlainLinksArray(link, _applyOptions, linksArray, returnLinksPathsById);
        }
        const virtualizedLinks = {};
        for (let l = 0; l < linksArray.length; l++) {
            let link = linksArray[l];
            if (!virtualizedLinks[link.id] && !link.from_id && !link.to_id) {
                const virtualId = this.virtualInverted[link.id];
                const compatable = virtualId ? [this.byId[virtualId]] : this.select({
                    type_id: virtualizedLinks[link.type_id] ? { _in: [link.type_id, virtualizedLinks[link.type_id].id] } : link.type_id,
                    _id: { _is_null: true },
                    limit: 1,
                });
                if (compatable[0]) {
                    virtualizedLinks[link.id] = compatable[0];
                }
            }
        }
        let needRepeatIdentification = true;
        while (needRepeatIdentification) {
            let hasIdentifiedVirtualizedPerCycle = false;
            for (let l = 0; l < linksArray.length; l++) {
                let link = linksArray[l];
                if (!virtualizedLinks[link.id]) {
                    if (link.from_id && link.to_id) {
                        const virtual = this.virtualInverted[link.id];
                        const compatable = this.select({
                            type_id: virtualizedLinks[link.type_id] ? { _in: [link.type_id, virtualizedLinks[link.type_id].id] } : link.type_id,
                            from_id: virtualizedLinks[link.from_id] ? { _in: [link.from_id, virtualizedLinks[link.from_id].id] } : link.from_id,
                            to_id: virtualizedLinks[link.to_id] ? { _in: [link.to_id, virtualizedLinks[link.to_id].id] } : link.to_id,
                            _id: { _is_null: true },
                            limit: 1,
                        });
                        if (compatable[0]) {
                            virtualizedLinks[link.id] = compatable[0];
                            hasIdentifiedVirtualizedPerCycle = true;
                        }
                    }
                }
            }
            if (!hasIdentifiedVirtualizedPerCycle)
                needRepeatIdentification = false;
        }
        for (let l = 0; l < linksArray.length; l++) {
            let link = linksArray[l];
            let old = byId[link.id];
            const virtual = virtualizedLinks[link.id] || (this.virtualInverted[link.id] ? this.byId[this.virtualInverted[link.id]] : undefined);
            if (virtual && !old) {
                old = virtual;
                if (!virtual._id) {
                    this.virtual[virtual.id] = link.id;
                    this.virtualInverted[link.id] = virtual.id;
                    virtual._id = link.id;
                    link = Object.assign(Object.assign({}, link), { _id: link.id, id: virtual.id });
                }
            }
            if (!old && !virtual) {
                link._applies = [applyName];
                if (namespace)
                    link._namespaces = [namespace];
                this.emitter.emit('apply', old, link);
                this.emitter.emit('+apply', old, link, applyName);
                toAdd.push(link);
            }
            else {
                if (!~old._applies.indexOf(applyName)) {
                    link._applies = old._applies = [...old._applies, applyName];
                    this.emitter.emit('apply', old, link);
                    this.emitter.emit('+apply', old, link, applyName);
                }
                else {
                    link._applies = old._applies;
                }
                if (!options.equal(old, link)) {
                    toUpdate.push(link);
                    beforeUpdate[link.id] = old;
                }
                if (namespace) {
                    if (!~old._namespaces.indexOf(namespace)) {
                        link._namespaces = old._namespaces = [...old._namespaces, namespace];
                    }
                    else {
                        link._namespaces = old._namespaces;
                    }
                }
            }
            _byId[link.id] = link;
        }
        for (let l = 0; l < links.length; l++) {
            const link = links[l];
            if (!_byId[link._id]) {
                const index = link._applies.indexOf(applyName);
                const isVirtualAndNeedToDelete = link._applies.length == 2 && link._applies.includes('');
                if (!!~index) {
                    if (link._applies.length === 1 || isVirtualAndNeedToDelete) {
                        toRemove.push(link);
                    }
                    else {
                        link._applies.splice(index, 1);
                        this.emitter.emit('apply', link, link);
                        this.emitter.emit('-apply', link, link, applyName);
                    }
                }
            }
        }
        const r1 = this.remove(toRemove.map(l => l[options.id]));
        const a1 = this.add(toAdd);
        this._updating = true;
        const r2 = this.remove(toUpdate.map(l => l[options.id]));
        const a2 = this.add(toUpdate);
        for (let i = 0; i < toUpdate.length; i++) {
            const l = toUpdate[i];
            this.emitter.emit('updated', beforeUpdate[l.id], byId[l.id]);
        }
        this._updating = false;
        return { errors: [...r1.errors, ...a1.errors, ...r2.errors, ...a2.errors], anomalies: [...r1.anomalies, ...a1.anomalies, ...r2.anomalies, ...a2.anomalies] };
    }
    update(linksArray) {
        log('update', linksArray, this);
        const { byId, byFrom, byTo, byType, types, links, options } = this;
        const toUpdate = [];
        const beforeUpdate = {};
        const _byId = {};
        for (let l = 0; l < linksArray.length; l++) {
            let link = linksArray[l];
            let old = byId[link.id];
            const virtualIds = Object.keys(this.virtual);
            const [virtual] = this.query(Object.assign(Object.assign(Object.assign(Object.assign({ id: { _in: virtualIds.map(i => Number(i)) } }, (link.type_id ? { _type_id: link.type_id } : {})), (link.from_id ? { _from_id: link.from_id } : {})), (link.to_id ? { _to_id: link.to_id } : {})), (link.value ? { value: link.value } : {})));
            if (virtual) {
                if (old)
                    throw new Error(`somehow we have oldLink.id ${old.id} and virtualLink.id ${virtual.id} virtualLink._id = ${virtual._id}`);
                old = virtual;
                virtual._id = link.id;
                link = Object.assign(Object.assign({}, link), { _id: link.id, id: virtual.id });
            }
            if (old) {
                if (!options.equal(old, link)) {
                    toUpdate.push(link);
                    beforeUpdate[link.id] = old;
                }
            }
            _byId[link.id] = link;
        }
        this._updating = true;
        const r2 = this.remove(toUpdate.map(l => l[options.id]));
        const a2 = this.add(toUpdate);
        for (let i = 0; i < toUpdate.length; i++) {
            const l = toUpdate[i];
            this.emitter.emit('updated', beforeUpdate[l.id], byId[l.id]);
        }
        this._updating = false;
        return { errors: [...r2.errors, ...a2.errors], anomalies: [...r2.anomalies, ...a2.anomalies] };
    }
    constructor(options, memory) {
        this.useMinilinksQuery = useMinilinksQuery;
        this.useMinilinksFilter = useMinilinksFilter;
        this.useMinilinksApply = useMinilinksApply;
        this.useMinilinksSubscription = useMinilinksSubscription;
        this.useMinilinksHandle = useMinilinksHandle;
        this.virtual = {};
        this.virtualInverted = {};
        this.virtualCounter = -1;
        this.types = {};
        this.byId = {};
        this.byFrom = {};
        this.byTo = {};
        this.byType = {};
        this.links = [];
        this._updating = false;
        const _options = options || MinilinksGeneratorOptionsDefault;
        this.types = this.byType = (memory === null || memory === void 0 ? void 0 : memory.types) || {};
        this.byId = (memory === null || memory === void 0 ? void 0 : memory.byId) || {};
        this.byFrom = (memory === null || memory === void 0 ? void 0 : memory.byFrom) || {};
        this.byTo = (memory === null || memory === void 0 ? void 0 : memory.byTo) || {};
        this.links = (memory === null || memory === void 0 ? void 0 : memory.links) || [];
        this.options = _options;
        this.emitter = new EventEmitter();
    }
}
export const minilinks = Minilinks(MinilinksGeneratorOptionsDefault);
export function useMinilinksConstruct(options) {
    const mlRef = useRef(useMemo(() => {
        return new MinilinkCollection(options);
    }, []));
    const ml = mlRef.current;
    return { ml, ref: mlRef };
}
export function useMinilinksFilter(ml, filter, results, interval) {
    const [state, setState] = useState();
    const action = useDebounceCallback((l, ml, ol, nl) => {
        setState(results(l, ml, ol, nl));
    }, 500, true);
    const refs = useRef({});
    useEffect(() => {
        if (refs.current.addedListener)
            ml.emitter.removeListener('added', refs.current.addedListener);
        if (refs.current.updatedListener)
            ml.emitter.removeListener('updated', refs.current.updatedListener);
        if (refs.current.removedListener)
            ml.emitter.removeListener('removed', refs.current.removedListener);
        if (refs.current.applyListener)
            ml.emitter.removeListener('apply', refs.current.applyListener);
        refs.current.addedListener = (ol, nl) => {
            if (filter(nl, ol, nl)) {
                action(nl, ml, ol, nl);
            }
        };
        ml.emitter.on('added', refs.current.addedListener);
        refs.current.updatedListener = (ol, nl) => {
            if (filter(nl, ol, nl)) {
                action(nl, ml, ol, nl);
            }
        };
        ml.emitter.on('updated', refs.current.updatedListener);
        refs.current.removedListener = (ol, nl) => {
            if (filter(ol, ol, nl)) {
                action(ol, ml, ol, nl);
            }
        };
        ml.emitter.on('removed', refs.current.removedListener);
        refs.current.applyListener = (ol, nl) => {
            if (filter(nl, ol, nl)) {
                action(nl, ml, ol, nl);
            }
        };
        let timeout;
        if (interval)
            timeout = setTimeout(() => {
                action(undefined, ml);
            }, interval);
        ml.emitter.on('apply', refs.current.applyListener);
        return () => {
            clearTimeout(timeout);
            ml.emitter.removeListener('added', refs.current.addedListener);
            ml.emitter.removeListener('updated', refs.current.updatedListener);
            ml.emitter.removeListener('removed', refs.current.removedListener);
            ml.emitter.removeListener('apply', refs.current.applyListener);
        };
    }, [ml]);
    useEffect(() => {
        setState(results(undefined, ml, undefined, undefined));
    }, [ml, filter, results]);
    return state;
}
;
export function useMinilinksHandle(ml, handler) {
    useEffect(() => {
        const addedListener = (ol, nl) => {
            handler('added', ol, nl);
        };
        ml.emitter.on('added', addedListener);
        const updatedListener = (ol, nl) => {
            handler('updated', ol, nl);
        };
        ml.emitter.on('updated', updatedListener);
        const removedListener = (ol, nl) => {
            handler('removed', ol, nl);
        };
        ml.emitter.on('removed', removedListener);
        const applyListener = (ol, nl) => {
            handler('apply', ol, nl);
        };
        ml.emitter.on('apply', applyListener);
        return () => {
            ml.emitter.removeListener('added', addedListener);
            ml.emitter.removeListener('updated', updatedListener);
            ml.emitter.removeListener('removed', removedListener);
            ml.emitter.removeListener('apply', applyListener);
        };
    }, []);
}
;
export function useMinilinksApply(ml, name, data) {
    const [strictName] = useState(name);
    useEffect(() => {
        return () => {
            ml.apply(Array.isArray(data) ? [] : Object.assign(Object.assign({}, data), { data: [] }), strictName);
        };
    }, []);
    ml.apply(data, strictName);
}
export function useMinilinksQuery(ml, query) {
    return useMemo(() => ml.query(query), [ml, query]);
}
;
export function useMinilinksSubscription(ml, query) {
    const [d, setD] = useState();
    const sRef = useRef();
    const qPrevRef = useRef(query);
    const q = useMemo(() => _isEqual(query, qPrevRef.current) ? qPrevRef.current : query, [query]);
    qPrevRef.current = q;
    useEffect(() => {
        setD(ml.query(q));
        if (sRef.current)
            sRef.current.unsubscribe();
        const obs = ml.subscribe(q);
        const sub = sRef.current = obs.subscribe({
            next: (links) => {
                setD(links);
            },
            error: (error) => {
                throw new Error(error);
            },
        });
        return () => {
            sub.unsubscribe();
        };
    }, [q]);
    return d || ml.query(q);
}
;
export function useMinilinksGenerator(minilinks) {
    const ref = useRef(minilinks);
    ref.current = useMemo(() => {
        if (ref.current)
            return ref.current;
        return new MinilinkCollection();
    }, []);
    return ref.current;
}
export const MinilinksContext = createContext(undefined);
export function MinilinksProvider({ minilinks: initialMinilinks, children, }) {
    const minilinks = useMinilinksGenerator(initialMinilinks);
    return (React.createElement(MinilinksContext.Provider, { value: minilinks }, children));
}
export function useMinilinks() {
    return useContext(MinilinksContext);
}
//# sourceMappingURL=minilinks.js.map