import { findInstance } from './findInstance';
import { getInstanceOfDraggableProvider, getInstanceOfDraggableSource, getInstanceOfDraggableTarget, } from './dragAndDropTestUtils';
import { dataAttributes } from '../../DragAndDrop/Draggable/constants';
import { dragAndDropPuppeteerDriverFactory } from './dragAndDropPuppeteerDriverFactory';
import { dragAndDropPlaywrightDriverFactory } from './dragAndDropPlaywrightDriverFactory';
import { act } from '../../utils/test-utils/unidriver/actCompat';
import { isPuppeteerUniDriver } from '../test-utils/unidriver/isPuppeteerUniDriver';
import { isPlaywrightUniDriver } from '../test-utils/unidriver/isPlaywrightUniDriver';
const getManager = instance => getInstanceOfDraggableProvider(instance).getManager();
const getDraggableId = (element, dataHook) => element
    .querySelector(`[data-hook="${dataHook}"]`)
    .getAttribute(dataAttributes.id);
const simulateMouseMove = (dndInstance, draggableId) => {
    getManager(dndInstance)
        .getBackend()
        .simulateHover([
        getInstanceOfDraggableTarget(dndInstance, draggableId).getHandlerId(),
    ]);
};
export const dragAndDropDriverFactory = ({ base, element }) => {
    const isPuppeteer = isPuppeteerUniDriver(base);
    const isPlaywright = isPlaywrightUniDriver(base);
    // Only create drivers when needed to avoid errors from incompatible APIs
    const getDragAndDropPuppeteerDriver = () => dragAndDropPuppeteerDriverFactory({ base, element });
    const getDragAndDropPlaywrightDriver = () => dragAndDropPlaywrightDriverFactory({ base });
    return {
        beginDrag: async ({ dataHook }) => {
            if (isPlaywright) {
                return getDragAndDropPlaywrightDriver().beginDrag({ dataHook });
            }
            if (isPuppeteer) {
                return getDragAndDropPuppeteerDriver().beginDrag({ dataHook });
            }
            const dndInstance = findInstance(element);
            const draggableId = getDraggableId(element, dataHook);
            const handleId = getInstanceOfDraggableSource(dndInstance, draggableId).getHandlerId();
            await act(async () => {
                getManager(dndInstance).getBackend().simulateBeginDrag([handleId]);
            });
        },
        dragOver: async ({ dataHook, offset }) => {
            if (isPlaywright) {
                return getDragAndDropPlaywrightDriver().dragOver({ dataHook, offset });
            }
            if (isPuppeteer) {
                return getDragAndDropPuppeteerDriver().dragOver({ dataHook, offset });
            }
            const dndInstance = findInstance(element);
            const draggableId = getDraggableId(element, dataHook);
            if (offset) {
                getManager(dndInstance).getMonitor().getClientOffset = vi.fn(() => offset);
                // on horizontal item move we have to simulate one more mouse move event
                // in order to get correct drop target
                await act(async () => {
                    simulateMouseMove(dndInstance, draggableId);
                });
            }
            await act(async () => {
                simulateMouseMove(dndInstance, draggableId);
            });
        },
        endDrag: async (item) => {
            if (isPlaywright) {
                return getDragAndDropPlaywrightDriver().endDrag();
            }
            if (isPuppeteer) {
                return getDragAndDropPuppeteerDriver().endDrag({
                    dataHook: item.dataHook,
                });
            }
            const backend = getManager(findInstance(element)).getBackend();
            await act(async () => {
                backend.simulateDrop();
                backend.simulateEndDrag();
            });
        },
        getItemPosition: async ({ dataHook }) => {
            const draggableItems = await base
                .$$(`[${dataAttributes.draggableTarget}]`)
                .map(async (item) => ({
                dataHook: await (await item.$$(`[${dataAttributes.draggableSource}]`).get(0)).attr('data-hook'),
            }));
            return draggableItems.findIndex(item => item.dataHook === dataHook);
        },
        getItemDepth: async ({ dataHook }) => {
            const depth = await base
                .$(`[data-hook="${dataHook}"]`)
                .attr(dataAttributes.depth);
            return parseInt(depth, 10);
        },
    };
};
const changeItemDepthByOneLevel = async ({ dragAndDropDriver, dataHook, offset, }) => {
    await dragAndDropDriver.beginDrag({ dataHook });
    await dragAndDropDriver.dragOver({
        dataHook,
        offset: {
            x: offset,
            y: 0,
        },
    });
    await dragAndDropDriver.endDrag({ dataHook });
};
export const changeItemDepth = async ({ dragAndDropDriver, depthLevel, threshold, dataHook, }) => {
    for (let i = 0; i < Math.abs(depthLevel); i++) {
        // we have to move one level at a time,
        // so the drop targets could be registered successfully
        await changeItemDepthByOneLevel({
            dragAndDropDriver,
            dataHook,
            offset: depthLevel > 0 ? threshold : threshold * -1,
        });
    }
};
//# sourceMappingURL=dragAndDropDriverFactory.js.map