import { Chain, Guard, NamedChain, Waiter } from '@ephox/agar';
import { Optional, Result } from '@ephox/katamari';
import { Css, Scroll, Traverse } from '@ephox/sugar';
import { assert } from 'chai';
import { Positioning } from 'ephox/alloy/api/behaviour/Positioning';
import * as Attachment from 'ephox/alloy/api/system/Attachment';
import * as ChainUtils from 'ephox/alloy/test/ChainUtils';
import * as Sinks from 'ephox/alloy/test/Sinks';
const addPopupToSinkCommon = (popup, sink, positioner) => {
    Attachment.attach(sink, popup);
    positioner();
};
const addPopupToSink = (popup, placementSpec, sink) => {
    const positioner = () => Positioning.position(sink, popup, placementSpec);
    addPopupToSinkCommon(popup, sink, positioner);
};
const addPopupToSinkWithin = (popup, placementSpec, sink, elem) => {
    const positioner = () => Positioning.positionWithin(sink, popup, placementSpec, Optional.some(elem));
    addPopupToSinkCommon(popup, sink, positioner);
};
const addPopupToSinkWithinBounds = (popup, placementSpec, sink, bounds) => {
    const positioner = () => Positioning.positionWithinBounds(sink, popup, placementSpec, Optional.some(bounds));
    addPopupToSinkCommon(popup, sink, positioner);
};
const pTestPopupInSink = (sinkName, popup, sink) => Waiter.pTryUntil(`Ensuring that the popup is inside the ${sinkName} sink`, () => {
    const inside = Sinks.isInside(sink, popup);
    assert.isTrue(inside, `The popup does not appear within the ${sinkName} sink container`);
});
const pTestPopupInViewport = (sinkName, popup) => Waiter.pTryUntil(`Ensuring that the popup is inside window viewport for the ${sinkName} sink`, () => {
    const bounds = popup.element.dom.getBoundingClientRect();
    const inside = bounds.top >= 0 && bounds.left >= 0 && bounds.top <= window.innerHeight && bounds.left <= window.innerWidth;
    assert.isTrue(inside, `The popup does not appear within the window viewport for the ${sinkName} sink`);
});
const pTestPopupPosition = async (sinkName, popup, sink) => {
    await pTestPopupInSink(sinkName, popup, sink);
    await pTestPopupInViewport(sinkName, popup);
};
const pTestSink = async (sinkName, sink, popup, placementSpec) => {
    addPopupToSink(popup, placementSpec, sink);
    await pTestPopupPosition(sinkName, popup, sink);
};
const pTestSinkWithin = async (sinkName, sink, popup, placementSpec, elem) => {
    addPopupToSinkWithin(popup, placementSpec, sink, elem);
    await pTestPopupPosition(sinkName, popup, sink);
};
const pTestSinkWithinBounds = async (sinkName, sink, popup, placementSpec, bounds) => {
    addPopupToSinkWithinBounds(popup, placementSpec, sink, bounds);
    await pTestPopupPosition(sinkName, popup, sink);
};
const addTopBottomMargin = (component, amount) => {
    Css.set(component.element, 'margin-top', amount);
    Css.set(component.element, 'margin-bottom', amount);
};
const cAddPopupToSink = (sinkName) => NamedChain.bundle((data) => {
    addPopupToSink(data.popup, { anchor: data.anchor }, data[sinkName]);
    return Result.value(data);
});
const cAddPopupToSinkWithin = (sinkName, elem) => NamedChain.bundle((data) => {
    addPopupToSinkWithin(data.popup, { anchor: data.anchor }, data[sinkName], elem);
    return Result.value(data);
});
const cAddPopupToSinkWithinBounds = (sinkName, bounds) => NamedChain.bundle((data) => {
    addPopupToSinkWithinBounds(data.popup, { anchor: data.anchor }, data[sinkName], bounds);
    return Result.value(data);
});
const cTestPopupInSink = (label, sinkName) => Chain.control(NamedChain.bundle((data) => {
    const sink = data[sinkName];
    const inside = Sinks.isInside(sink, data.popup);
    return inside ? Result.value(data) : Result.error(new Error('The popup does not appear within the ' + label + ' sink container'));
}), Guard.tryUntil('Ensuring that the popup is inside the ' + label + ' sink'));
const cTestPopupInViewport = (sinkName) => Chain.control(NamedChain.bundle((data) => {
    const bounds = data.popup.element.dom.getBoundingClientRect();
    const inside = bounds.top >= 0 && bounds.left >= 0 && bounds.top <= window.innerHeight && bounds.left <= window.innerWidth;
    return inside ? Result.value(data) : Result.error(new Error('The popup does not appear within window viewport for the ' + sinkName + ' sink'));
}), Guard.tryUntil('Ensuring that the popup is inside window viewport for the ' + sinkName + ' sink'));
const cScrollTo = Chain.mapper((component) => {
    component.element.dom.scrollIntoView();
    const doc = Traverse.owner(component.element);
    return Scroll.get(doc);
});
const cAddTopBottomMargin = (amount) => Chain.op((component) => {
    addTopBottomMargin(component, amount);
});
const cTestSinkPopupPosition = (sinkName) => Chain.fromChains([
    cTestPopupInSink(sinkName, sinkName),
    cTestPopupInViewport(sinkName)
]);
const cTestSink = (label, sinkName) => ChainUtils.cLogging(label, [
    cAddPopupToSink(sinkName),
    cTestSinkPopupPosition(sinkName)
]);
const cTestSinkWithin = (label, sinkName, elem) => ChainUtils.cLogging(label, [
    cAddPopupToSinkWithin(sinkName, elem),
    cTestSinkPopupPosition(sinkName)
]);
const cTestSinkWithinBounds = (label, sinkName, bounds) => ChainUtils.cLogging(label, [
    cAddPopupToSinkWithinBounds(sinkName, bounds),
    cTestSinkPopupPosition(sinkName)
]);
const cScrollDown = (componentName, amount) => ChainUtils.cLogging('Adding margin to ' + componentName + ' and scrolling to it', [
    NamedChain.direct(componentName, cAddTopBottomMargin(amount), '_'),
    NamedChain.direct(componentName, cScrollTo, '_')
]);
export { cTestSink, cTestSinkWithin, cTestSinkWithinBounds, cScrollDown, pTestSink, pTestSinkWithin, pTestSinkWithinBounds };
//# sourceMappingURL=PositionTestUtils.js.map