import ReactDOM from 'react-dom';
import debounce from 'lodash.debounce';
import warning from 'warning';
import { PORTAL_INLET_ATTR, PORTAL_OUTLET_ATTR } from '../internal/RenderContainer/index.js';
import { getOwnerGlobalObject } from '../lib/globalObject.js';
import { isInstanceOf } from './isInstanceOf.js';
import { isFirefox } from './client.js';
var handlers = [];
var listenersAttached = new WeakSet();
function addHandleEvent(globalObject) {
    var _a;
    /**
     * Firefox do not supports 'focusin' event.
     * Focus events bubbles multiple time
     * without possibilty to cancell bubbling.
     * Using debounce to capture only first focus event
     * Mozilla Firefix
     *   ¯\_(ツ)_/¯
     */
    (_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.body.addEventListener(isFirefox ? 'focus' : 'focusin', isFirefox ? debounce(handleNativeFocus, 0, { leading: true, trailing: false }) : handleNativeFocus, { capture: true });
}
function addFocusListener(globalObject) {
    var _a, _b;
    if (listenersAttached.has(globalObject)) {
        return;
    }
    if (((_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.readyState) === 'complete') {
        addHandleEvent(globalObject);
    }
    else {
        (_b = globalObject.addEventListener) === null || _b === void 0 ? void 0 : _b.call(globalObject, 'load', function () { return addHandleEvent(globalObject); });
    }
    listenersAttached.add(globalObject);
}
function handleNativeFocus(event) {
    // FIXME: not safe casting
    var target = (event.target || event.srcElement);
    handlers.forEach(function (handler) {
        var elements = handler.elements;
        if (typeof elements === 'function') {
            elements = elements();
        }
        if (elements.some(containsTargetOrRenderContainer(target))) {
            return;
        }
        ReactDOM.unstable_batchedUpdates(function () { return handler.callback(event); });
    });
}
export function containsTargetOrRenderContainer(target) {
    return function (element) {
        if (!element) {
            return false;
        }
        if (element.contains(target)) {
            return true;
        }
        var container = findRenderContainer(target, element);
        return !!container && element.contains(container);
    };
}
/**
 * Searches RenderContainer placed in "rootNode" for "node"
 */
export function findRenderContainer(node, rootNode, container) {
    var _a, _b, _c;
    var currentNode = node.parentNode;
    var globalObject = getOwnerGlobalObject(node);
    if (!currentNode ||
        node === rootNode ||
        currentNode === rootNode ||
        currentNode === ((_a = globalObject.document) === null || _a === void 0 ? void 0 : _a.body) ||
        currentNode === ((_b = globalObject.document) === null || _b === void 0 ? void 0 : _b.documentElement) ||
        !isInstanceOf(currentNode, globalObject.Element)) {
        return container ? container : null;
    }
    var newContainerId = currentNode.getAttribute(PORTAL_OUTLET_ATTR);
    if (newContainerId) {
        var nextNode = (_c = globalObject.document) === null || _c === void 0 ? void 0 : _c.querySelector("[".concat(PORTAL_INLET_ATTR, "~=\"").concat(newContainerId, "\"]"));
        if (!nextNode) {
            warning(false, "Origin node for render container was not found");
            return null;
        }
        return findRenderContainer(nextNode, rootNode, nextNode);
    }
    return findRenderContainer(currentNode, rootNode, container);
}
export function listen(elements, callback, globalObject) {
    addFocusListener(globalObject);
    var handler = {
        elements: elements,
        callback: callback,
    };
    handlers.push(handler);
    return {
        remove: function () {
            var index = handlers.indexOf(handler);
            if (index > -1) {
                handlers.splice(index, 1);
            }
        },
    };
}
//# sourceMappingURL=listenFocusOutside.js.map