import { baseUniDriverFactory, ReactBase, findByHook, getDataAttributeValue, isElementFocused, } from '../utils/test-utils/unidriver';
import { DATA_OPTION, DATA_HOOKS, DATA_DIRECTION, DATA_SHOWN, DROPDOWN_LAYOUT_DIRECTIONS, OPTION_DATA_HOOKS, DATA_SELECTED_OPTION_ID, } from './DataAttr';
export const dropdownLayoutDriverFactory = (base, body) => {
    const reactBase = ReactBase(base);
    const contentContainer = findByHook(base, DATA_HOOKS.CONTENT_CONTAINER);
    const infiniteScrollContainer = () => findByHook(base, DATA_HOOKS.INFINITE_SCROLL_CONTAINER);
    const optionsElement = () => findByHook(base, DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS);
    async function getOptionsContainerDataHook() {
        return (await infiniteScrollContainer().exists())
            ? DATA_HOOKS.INFINITE_SCROLL_CONTAINER
            : DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS;
    }
    const optionElementAt = async (position) => base.$(`[data-hook=${await getOptionsContainerDataHook()}] > *:nth-child(${position + 1})`);
    const optionElementContentAt = async (position) => base.$(`[data-list-type="action"] [data-hook=${await getOptionsContainerDataHook()}] > *:nth-child(${position + 1}) > *`);
    const optionElementByDataHook = async (dataHook) => base.$(`[data-hook=${await getOptionsContainerDataHook()}] [data-hook="${dataHook}"]`);
    const options = async () => {
        return base
            .$$(`[data-hook=${await getOptionsContainerDataHook()}] > *`)
            .map(child => Promise.resolve(child));
    };
    const optionsLength = async () => (await options()).length;
    const doIfOptionExists = async (position, onSuccess) => {
        if ((await optionsLength()) <= position) {
            throw new Error(`index out of bounds, try to get option ${position} while only ${optionsLength()} options exists`);
        }
        return onSuccess();
    };
    const getOptionDriver = (position) => doIfOptionExists(position, async () => {
        const optionElementContent = await optionElementContentAt(position);
        const option = (await optionElementContent.exists())
            ? optionElementContent
            : await optionElementAt(position);
        return createOptionDriver(option);
    });
    return {
        ...baseUniDriverFactory(base),
        /** @deprecated should be private */
        classes: (_name) => optionsElement()._prop('className'),
        /** Clicks on an option at a specific index
         * @param {number} option index
         * @return {Promise<void>}
         */
        clickAtOption: async (index) => (await getOptionDriver(index)).click(),
        /** Clicks on an option with a specific dataHook
         * @param {string} dataHook
         * @return {Promise<void>}
         */
        clickAtOptionByDataHook: async (dataHook) => (await optionElementByDataHook(dataHook)).click(),
        /** Clicks on an option with a specific value
         * @param {string} value
         * @return {Promise<void>}
         */
        clickAtOptionWithValue: async (value) => {
            for (const option of await options()) {
                const optionDriver = await createOptionDriver(option);
                if ((await optionDriver.content()) === value) {
                    return optionDriver.click();
                }
            }
        },
        /** @deprecated deprecated prop */
        hasTopArrow: async () => await findByHook(base, DATA_HOOKS.TOP_ARROW).exists(),
        /** @deprecated deprecated prop */
        isDown: async () => (await contentContainer.attr(DATA_DIRECTION)) ===
            DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
        /** @deprecated deprecated prop */
        isUp: async () => (await contentContainer.attr(DATA_DIRECTION)) ===
            DROPDOWN_LAYOUT_DIRECTIONS.UP,
        isLinkOption: async (position) => doIfOptionExists(position, async () => {
            const optionDriver = await getOptionDriver(position);
            return optionDriver.isLink();
        }),
        isOptionADivider: (position) => doIfOptionExists(position, async () => {
            const optionDriver = await getOptionDriver(position);
            return optionDriver.isDivider();
        }),
        isOptionExists: async (optionText) => {
            for (const _option of await options()) {
                if ((await _option.text()) === optionText) {
                    return true;
                }
            }
            return false;
        },
        isOptionHovered: async (index) => doIfOptionExists(index, async () => {
            const optionDriver = await getOptionDriver(index);
            return optionDriver.isHovered();
        }),
        isOptionSelected: async (index) => doIfOptionExists(index, async () => {
            const optionDriver = await getOptionDriver(index);
            return optionDriver.isSelected();
        }),
        /** @deprecated */
        isOptionHeightSmall: (position) => doIfOptionExists(position, async () => (await (await optionElementAt(position)).attr(DATA_OPTION.SIZE)) ===
            'small'),
        /** @deprecated */
        isOptionHeightBig: (position) => doIfOptionExists(position, async () => (await (await optionElementAt(position)).attr(DATA_OPTION.SIZE)) ===
            'big'),
        isListItemFocused: async (position) => isElementFocused(await optionElementContentAt(position)),
        isShown: async () => !!(await contentContainer.attr(DATA_SHOWN)),
        mouseEnter: () => base.hover(),
        mouseEnterAtOption: (position) => doIfOptionExists(position, async () => {
            const optionDriver = await getOptionDriver(position);
            return optionDriver.mouseEnter();
        }),
        mouseLeave: async () => {
            switch (base.type) {
                case 'react':
                    await reactBase.mouseLeave();
                    return;
                case 'puppeteer':
                    const { element } = await baseUniDriverFactory(base).element();
                    await page.evaluate(el => {
                        el.dispatchEvent(new MouseEvent('mouseout', {
                            bubbles: true,
                            view: window,
                            cancelable: true,
                        }));
                    }, element);
                    return;
                default:
                    return;
            }
        },
        /** @deprecated deprecated prop */
        mouseClickOutside: () => ReactBase.clickBody(),
        mouseLeaveAtOption: (position) => doIfOptionExists(position, async () => {
            const optionDriver = await getOptionDriver(position);
            return optionDriver.mouseLeave();
        }),
        /** @deprecated Use optionDriver*/
        optionAt: () => {
            // @ts-expect-error
            return optionElementAt.getNative();
        },
        // This should be a private method since the hook include internal parts ('dropdown-divider-{id}, dropdown-item-{id})') */
        /** @deprecated */
        optionByHook: async (hook) => {
            const option = optionsElement().$(`[data-hook=${hook}]`);
            if (!(await option.exists())) {
                throw new Error(`an option with data-hook ${hook} was not found`);
            }
            return createOptionDriver(option);
        },
        /**
         * Get Option by id
         * @returns {Promise<DropdownLayoutOptionUniDriver>}
         */
        optionById(optionId) {
            return this.optionByHook(`dropdown-item-${optionId}`);
        },
        optionContentAt: (position) => doIfOptionExists(position, async () => {
            const optionDriver = await getOptionDriver(position);
            return optionDriver.content();
        }),
        optionDriver: createOptionDriver,
        /** Get Options drivers */
        options: async () => {
            const drivers = [];
            for (let position = 0; position < (await optionsLength()); position++) {
                drivers.push(await getOptionDriver(position));
            }
            return drivers;
        },
        optionsContent: async () => {
            const contentArray = [];
            for (const option of await options()) {
                const optionDriver = await createOptionDriver(option);
                contentArray.push(await optionDriver.content());
            }
            return contentArray;
        },
        markedOption: async () => {
            const allOptions = await options();
            const optionsWithHovered = await Promise.all(allOptions.map(async (option) => ({
                option,
                hovered: !!(await option.attr(DATA_OPTION.HOVERED)),
            })));
            const hoveredOptions = optionsWithHovered
                .filter(({ hovered }) => hovered)
                .map(({ option }) => option);
            return ((hoveredOptions.length &&
                createOptionDriver(hoveredOptions[0]).content()) ||
                null);
        },
        getSelectedOptionId: async () => await getDataAttributeValue(contentContainer, DATA_SELECTED_OPTION_ID),
        optionsLength,
        /** @deprecated should be private */
        optionsScrollTop: () => optionsElement()._prop('scrollTop'),
        pressDownKey: () => base.pressKey('ArrowDown'),
        pressUpKey: () => base.pressKey('ArrowUp'),
        pressRightKey: () => base.pressKey('ArrowRight'),
        pressLeftKey: () => base.pressKey('ArrowLeft'),
        pressEnterKey: () => base.pressKey('Enter'),
        pressSpaceKey: () => base.pressKey(' '),
        pressTabKey: () => base.pressKey('Tab'),
        pressEscKey: () => base.pressKey('Escape'),
        keyDownListItem: async (position, key) => {
            const optionDriver = await getOptionDriver(position);
            return (await optionDriver.element()).pressKey(key);
        },
        tabIndex: () => base._prop('tabIndex'),
        getListItemAttribute: async (position, attribute) => {
            const optionDriver = await getOptionDriver(position);
            return (await optionDriver.element()).attr(attribute);
        },
        /** Returns the list type prop value */
        getListType: (dataHook) => findByHook(body, dataHook).attr('data-list-type'),
        getOptionsContainerRole: () => optionsElement().attr('role'),
    };
};
const createOptionDriver = (option) => ({
    element: () => option,
    mouseEnter: () => option.hover(),
    mouseLeave: () => ReactBase(option).mouseLeave(),
    isHovered: async () => !!(await option.attr(DATA_OPTION.HOVERED)),
    isSelected: async () => !!(await option.attr(DATA_OPTION.SELECTED)),
    content: () => option.text(),
    click: () => option.click(),
    isDivider: async () => {
        const divider = await findByHook(option, OPTION_DATA_HOOKS.DIVIDER);
        return divider.exists();
    },
    isDisabled: async () => !!(await option.attr(DATA_OPTION.DISABLED)),
    isLink: async () => (await option._prop('tagName')).toLowerCase() === 'a',
});
//# sourceMappingURL=DropdownLayout.uni.driver.js.map