import { CollectionTableUniDriver } from '../CollectionTable/CollectionTable.uni.driver';
import { waitFor } from '@wix/wix-ui-test-utils/unidriver';
import { LoaderUniDriver, MessageModalLayoutUniDriver, TextButtonUniDriver, } from '@wix/design-system/dist/testkit/unidriver';
import { DragAndDropUniDriver } from '../DragAndDrop/DragAndDrop.uni.driver';
import { ToolbarCollectionErrorStateUniDriver } from '../ErrorState/ToolbarCollectionErrorState.uni.driver';
import { CustomFieldModalUniDriver } from '../CustomFieldModal/CustomFieldModal.uni.driver';
import { ArchiveFieldModalUniDriver } from '../CustomFieldsPanel/ArchiveFieldModal.uni.driver';
import { baseUniDriverFactory, isSled2, isSled3 } from '../../unidriver';
import { ManageTagsModalUniDriver } from '../Tags/ManageTags/ManageTagsModal/ManageTagsModal.uni.driver';
import TagsAssignButtonUniDriver from '../TagsAssignButton/TagsAssignButton.uni.driver';
import { CollectionEmptyStateUniDriver } from '../EmptyState/CollectionEmptyState.uni.driver';
import { CollectionPremiumEmptyStateUniDriver } from '../EmptyState/CollectionPremiumEmptyState.uni.driver';
import { CollectionNoResultsStateUniDriver } from '../EmptyState/CollectionNoResultsState.uni.driver';
import { CollectionSectionHeaderUniDriver } from '../CollectionSectionHeader/CollectionSectionHeader.uni.driver';
export function TableUniDriver(base, body) {
    const collectionTable = CollectionTableUniDriver(base.$('[data-hook="collection-table"]'), body);
    const initialLoaderBase = () => base.$(`[data-hook="placeholder-states-loading-state"]`);
    const initialLoader = () => initialLoaderBase();
    const infiniteScrollLoaderBase = () => base.$(`[data-hook="infinite-scroll-loader"]`);
    const infiniteScrollLoader = () => LoaderUniDriver(infiniteScrollLoaderBase(), body);
    const getEmptyStateByDataHook = (dataHook = 'collection-empty-state') => CollectionEmptyStateUniDriver(base.$(`[data-hook="${dataHook}"]`), body);
    const getPremiumEmptyStateByDataHook = (dataHook) => CollectionPremiumEmptyStateUniDriver(base.$(`[data-hook="${dataHook}"]`), body);
    const getNoResultsStateByDataHook = (dataHook = 'collection-empty-state') => CollectionNoResultsStateUniDriver(base.$(`[data-hook="${dataHook}"]`), body);
    const getErrorStateByDataHook = (dataHook = 'placeholder-states-error-state') => ToolbarCollectionErrorStateUniDriver(base.$(`[data-hook="${dataHook}"]`), body);
    const viewsToolbarItem = () => base.$('[data-hook="collection-table-toolbar-views-item"]');
    const scrollUp = async (scrollableElementDataHook = 'page-scrollable-content', options = {}) => {
        const tableContent = base.$('[data-hook="table-content"]');
        const tableContentElement = await tableContent.getNative();
        // puppeteer
        if (isSled2(tableContentElement)) {
            const { page } = tableContentElement;
            let up;
            // if table is virtual by default we scroll down to the not rendered items, if `options.top` not passed
            if (await isTableVirtualized()) {
                const rowsCount = await getRenderedRowsCount();
                const avgRowHeight = +((await getAverageRowHeight()) ?? 0);
                up = rowsCount * avgRowHeight;
            }
            await page.evaluate((_dataHook, _options) => {
                const scrollableElement = document.querySelector(`[data-hook="${_dataHook}"]`);
                const optionsTopVal = _options.top === 0 ? -scrollableElement.scrollTop : _options.top;
                scrollableElement.scrollBy({
                    behavior: 'smooth',
                    top: optionsTopVal ?? _options.up,
                });
            }, scrollableElementDataHook, { ...options, up: -(up ?? 0) });
            await page.waitForTimeout(1000); // delay scroll resolve by 1 sec
        }
        // playwright
        else if (isSled3(tableContentElement)) {
            let up;
            if (await isTableVirtualized()) {
                const rowsCount = await getRenderedRowsCount();
                const avgRowHeight = +((await getAverageRowHeight()) ?? 0);
                up = rowsCount * avgRowHeight;
            }
            const locator = tableContentElement;
            const pwPage = locator.page();
            await pwPage.evaluate(({ dataHook, top: targetTop, up: upDist }) => {
                const scrollableElement = document.querySelector(`[data-hook="${dataHook}"]`);
                const optionsTopVal = targetTop === 0 ? -scrollableElement.scrollTop : targetTop;
                scrollableElement.scrollBy({
                    behavior: 'smooth',
                    top: optionsTopVal ?? upDist,
                });
            }, {
                dataHook: scrollableElementDataHook,
                top: options.top,
                up: -(up ?? 0),
            });
            await pwPage.waitForTimeout(1000);
        }
        // jsdom
        else {
            const pageScrollContent = body.$(`[data-hook="${scrollableElementDataHook}"]`);
            if (await pageScrollContent.exists()) {
                await (await pageScrollContent.getNative()).dispatchEvent(Object.assign(new Event('scroll'), {
                    _testScroll: options.top === 0 ? 'top' : 'up',
                }));
            }
        }
    };
    const scrollDown = async (scrollableElementDataHook = 'page-scrollable-content', options = {}) => {
        const tableContent = base.$('[data-hook="table-content"]');
        const tableContentElement = await tableContent.getNative();
        // puppeteer
        if (isSled2(tableContentElement)) {
            const { page } = tableContentElement;
            let top;
            // if table is virtual by default we scroll down to the not rendered items, if `options.top` not passed
            if (await isTableVirtualized()) {
                const rowsCount = await getRenderedRowsCount();
                const avgRowHeight = +((await getAverageRowHeight()) ?? 0);
                top = rowsCount * avgRowHeight;
            }
            await page.evaluate((_dataHook, _options) => {
                const scrollableElement = document.querySelector(`[data-hook="${_dataHook}"]`);
                scrollableElement.scrollBy({
                    behavior: 'smooth',
                    top: _options.top ?? scrollableElement.scrollHeight,
                });
            }, scrollableElementDataHook, { ...options, top: options.top ?? top ?? undefined });
            await page.waitForTimeout(1000); // delay scroll resolve by 1 sec
        }
        // playwright
        else if (isSled3(tableContentElement)) {
            let top;
            if (await isTableVirtualized()) {
                const rowsCount = await getRenderedRowsCount();
                const avgRowHeight = +((await getAverageRowHeight()) ?? 0);
                top = rowsCount * avgRowHeight;
            }
            const locator = tableContentElement;
            const pwPage = locator.page();
            await pwPage.evaluate(({ dataHook, top: targetTop }) => {
                const scrollableElement = document.querySelector(`[data-hook="${dataHook}"]`);
                scrollableElement.scrollBy({
                    behavior: 'smooth',
                    top: targetTop ?? scrollableElement.scrollHeight,
                });
            }, {
                dataHook: scrollableElementDataHook,
                top: options.top ?? top ?? undefined,
            });
            await pwPage.waitForTimeout(1000);
        }
        // jsdom
        else {
            const pageScrollContent = body.$(`[data-hook="${scrollableElementDataHook}"]`);
            if (await pageScrollContent.exists()) {
                const native = await pageScrollContent.getNative();
                native.dispatchEvent(new Event('scroll'));
            }
        }
    };
    const getVirtualizedBody = () => base.$('[data-hook="virtual-inner-element"]');
    const isTableVirtualized = () => getVirtualizedBody().exists();
    const getRows = () => base.$$('tbody tr[data-table-row="dataTableRow"]');
    const getAverageRowHeight = async () => {
        const visibleRow = await (await getRows()).get(0);
        return (await visibleRow.attr('style'))
            ?.split('height: ')[1]
            .split('px')[0];
    };
    const _getRowByKey = (rowKey) => base.$(`tr[data-hook="table-row-${rowKey}"]`);
    const getRenderedItemIndexAt = async (i) => {
        const row = getRows().get(i);
        const dataIndexAttr = await row.attr('data-index');
        const index = Number(dataIndexAttr?.replace(/^index-/, ''));
        return Number.isInteger(index) ? index : -1;
    };
    const getRenderedItemKeyAt = async (i) => {
        const row = getRows().get(i);
        const dataHookAttr = await row.attr('data-hook');
        return dataHookAttr?.split('table-row-')[1];
    };
    const getRenderedItemIndexByKey = async (rowKey) => {
        const row = _getRowByKey(rowKey);
        const dataIndexAttr = await row.attr('data-index');
        const index = Number(dataIndexAttr?.replace(/^index-/, ''));
        return Number.isInteger(index) ? index : -1;
    };
    const getLastRenderedItemIndex = async () => getRenderedItemIndexAt((await getRows().count()) - 1);
    const getLastRenderedItemKey = async () => getRenderedItemKeyAt((await getRows().count()) - 1);
    const getFirstRenderedItemKey = async () => getRenderedItemKeyAt(0);
    const getFirstRenderedItemIndex = async () => getRenderedItemIndexAt(0);
    // works for both virtual & non-virtual tables with WDS >= 1.81.0
    const getRowByIndex = (index) => base.$(`tr[data-index="index-${index}"]`);
    const getVirtualizedRowsCount = async () => {
        if (await isTableVirtualized()) {
            const str = await getVirtualizedBody().attr('data-count');
            return !isNaN(Number(str)) ? Number(str) : 0;
        }
        return 0;
    };
    const getRenderedRowsCount = async () => (await isTableVirtualized())
        ? getVirtualizedBody().$$(`tr[data-table-row="dataTableRow"]`).count()
        : collectionTable.getRowsCount();
    const getRowCheckbox = (index) => {
        return getRowByIndex(index)
            ?.$('[data-hook="bulk-selection-cell"]')
            ?.$('[data-hook="row-select"]');
    };
    const getRowKey = async (rowIndex, dataHook = 'table-row') => {
        const [attr] = await getRowsKeys([rowIndex], dataHook);
        return attr;
    };
    const getRowsKeys = async (rowsIndexes, dataHook = 'table-row') => {
        return Promise.all(rowsIndexes.map(async (rowIndex) => {
            const dataHookAttrValue = await (await getRowByIndex(rowIndex)).attr('data-hook');
            return dataHookAttrValue?.split(`${dataHook}-`)[1];
        }));
    };
    const getDragAndDrop = () => DragAndDropUniDriver(base);
    const getNavigationModalBase = () => body.$('[data-hook="navigation-modal"]');
    const getNavigationModal = () => ({
        ...MessageModalLayoutUniDriver(getNavigationModalBase(), body),
        getContent: () => getNavigationModalBase().$('[data-hook="action-modal-content"]'),
    });
    const getCustomFieldModal = () => CustomFieldModalUniDriver(base, body);
    const getArchiveFieldModal = () => ArchiveFieldModalUniDriver(base, body);
    const getManageTagsModal = () => ManageTagsModalUniDriver(base, body);
    const getTagsAssignButton = () => TagsAssignButtonUniDriver(base, body);
    const scrollDownUntil = (fn, { timeout = 7000, delay = 1000, scrollableElementDataHook = 'page-scrollable-content', top, } = {}) => {
        return waitFor(async () => {
            if (await fn()) {
                return true;
            }
            await scrollDown(scrollableElementDataHook, { top });
            return fn();
        }, timeout, delay);
    };
    const scrollUpUntil = async (fn, { timeout = 5000, delay = 1000, scrollableElementDataHook = 'page-scrollable-content', } = {}) => {
        return waitFor(async () => {
            if (await fn()) {
                return true;
            }
            await scrollUp(scrollableElementDataHook);
            return fn();
        }, timeout, delay);
    };
    const scrollUpToItem = (index, options = {}) => scrollUpUntil(() => getRowByIndex(index).exists(), options);
    const scrollUpToItemByKey = (rowKey, options = {}) => scrollUpUntil(async () => _getRowByKey(rowKey).exists(), options);
    const scrollDownToItem = (index, options = {}) => scrollDownUntil(async () => {
        await isTableVirtualized();
        return (await getRowByIndex(index)).exists();
    }, options);
    const scrollDownToItemByKey = (rowKey, options = {}) => scrollDownUntil(async () => _getRowByKey(rowKey).exists(), options);
    return {
        ...collectionTable,
        /**
         * Wait until root element to exist
         * @param timeout - time to wait until bailing
         */
        wait: (timeout) => base.wait(timeout),
        /** Wait for the initial fetch loader to be shown */
        waitInitialLoader: (timeout) => initialLoaderBase().wait(timeout),
        /** Wait for the initial fetch loader to be removed */
        waitInitialLoaderRemoved: (timeout, delay) => waitFor(async () => !(await initialLoaderBase().exists()), timeout, delay),
        /** Indicates whether initial loader is in loading status */
        isInitialLoaderLoading: () => initialLoader().exists(),
        /** Indicates whether initial loader exists */
        initialLoaderExists: () => initialLoader().exists(),
        /** Wait for the infinite scroll loader to be shown */
        waitInfiniteScrollLoader: (timeout) => infiniteScrollLoaderBase().wait(timeout),
        /** Indicates whether infinite scroll loader is in loading status */
        isInfiniteScrollLoaderLoading: () => infiniteScrollLoader().isLoading(),
        /** Indicates whether infinite scroll loader exists */
        infiniteScrollLoaderExists: () => infiniteScrollLoader().exists(),
        getInfiniteScrollLoaderStatusMessage: () => infiniteScrollLoader().getStatusMessage(),
        getErrorStateByDataHook,
        /** Get initial error state before table renders */
        getCardErrorStateByDataHook: (dataHook = 'collection-card-error-state') => getErrorStateByDataHook(dataHook),
        getTableDefaultNoResultsState: () => getNoResultsStateByDataHook(),
        getTableDefaultEmptyState: () => getEmptyStateByDataHook(),
        /** Get empty state within the table that by a specific data hook */
        getEmptyStateByDataHook,
        /** Get premium empty state within the table that by a specific data hook */
        getPremiumEmptyStateByDataHook,
        /** Get no results state within the table that by a specific data hook */
        getNoResultsStateByDataHook,
        viewsToolbarItemLabelExists: () => viewsToolbarItem().$('span').exists(),
        getEmptyStateClearFiltersButton: (emptyStateDataHook) => TextButtonUniDriver(base
            .$(`[data-hook="${emptyStateDataHook}"]`)
            .$('[data-hook="empty-state-clear-filters-button"]'), body),
        /**
         * Triggers "scroll" event on the scroll element. For the virtual table by default scrolls down to the not rendered items.
         * @param scrollableElementDataHook - The data hook of the scrollable element (Optional, defaults to "page-scrollable-content").
         */
        scrollDown,
        scrollHorizontally: async (options = {}) => {
            const tableContent = base.$('[data-hook="table-content"]');
            const tableContentElement = await tableContent.getNative();
            // puppeteer
            if (isSled2(tableContentElement)) {
                const page = options.page ?? tableContentElement.page;
                page.evaluate((_dataHook) => {
                    const scrollableElement = document.querySelector(`[data-hook="${_dataHook}"]`);
                    scrollableElement.scrollBy({
                        left: scrollableElement.scrollWidth,
                    });
                }, 'table-content');
            }
            // playwright
            else if (isSled3(tableContentElement)) {
                const locator = tableContentElement;
                await locator.page().evaluate((dataHook) => {
                    const scrollableElement = document.querySelector(`[data-hook="${dataHook}"]`);
                    scrollableElement.scrollBy({
                        left: scrollableElement.scrollWidth,
                    });
                }, 'table-content');
            }
        },
        /**
         * Triggers "scroll" event on the scroll element, until item exists
         * @param index
         * @param timeout - how much to wait for item exists
         * @param delay - how much to wait between each check
         * @param scrollableElementDataHook
         */
        scrollDownToItem,
        scrollDownToItemByKey,
        scrollDownTo: (row) => scrollDownUntil(async () => row.exists()),
        scrollUpTo: (row) => scrollUpUntil(async () => row.exists()),
        /**
         * Triggers "scroll" up event on the scroll element.
         * @param scrollableElementDataHook - The data hook of the scrollable element (Optional, defaults to "page-scrollable-content").
         */
        scrollUp,
        /**
         * Triggers "scroll" up event on the scroll element, until item exists
         * @param index
         * @param timeout - how much to wait for item exists
         * @param delay - how much to wait between each check
         * @param scrollableElementDataHook
         */
        scrollUpToItem,
        scrollUpToItemByKey,
        /**
         * Triggers "scroll" event on the scroll element to the top.
         * @param scrollableElementDataHook - The data hook of the scrollable element (Optional, defaults to "page-scrollable-content").
         */
        scrollToTop: async ({ scrollableElementDataHook = 'page-scrollable-content', } = {}) => {
            await scrollUp(scrollableElementDataHook, { top: 0 });
        },
        hoverRowByIndex: (index) => {
            return getRowByIndex(index).hover();
        },
        getRowByIndex,
        getCellTextValue: (index, col) => getRowByIndex(index).$$('td').get(col).text(),
        _getRowByKey,
        _getCellByKey: (rowKey, cellDataHook) => {
            return _getRowByKey(rowKey).$(`td[data-hook="${cellDataHook}"]`);
        },
        _getCellByRowIndexAndCellKey: (rowIndex, cellDataHook) => {
            return getRowByIndex(rowIndex).$(`td[data-hook="${cellDataHook}"]`);
        },
        /** Gets a driver by row number and column index */
        getCellDriverByIndex: (rowNum, col) => {
            return baseUniDriverFactory(collectionTable.getRow(rowNum).$$('td').get(col));
        },
        /** Gets a driver by row number and column data-hook */
        getCellDriverByDataHook: (rowNum, dataHook) => {
            return baseUniDriverFactory(collectionTable.getRow(rowNum).$(`td[data-hook="${dataHook}"]`));
        },
        clickRowCheckbox: async (index) => {
            const checkbox = await getRowCheckbox(index);
            await checkbox.click();
        },
        /**
         * Gets number of rendered rows (not all items are rendered for virtualized table)
         */
        getRenderedRowsCount,
        /**
         * Gets number of loaded items for virtualized table (not all items are rendered)
         */
        getVirtualizedRowsCount,
        /** Gets number of loaded items for both virtual and non-virtual table */
        getRowsCount: async () => (await isTableVirtualized())
            ? getVirtualizedRowsCount()
            : collectionTable.getRowsCount(),
        getVirtualizedBody,
        isTableVirtualized,
        getLastRenderedItemIndex,
        getLastRenderedItemKey,
        getFirstRenderedItemKey,
        getFirstRenderedItemIndex,
        getRenderedItemIndexAt,
        getRenderedItemIndexByKey,
        /** Get the row key according to the `itemKey` function */
        getRowKey,
        /** Get multiple rows keys according to the `itemKey` function */
        getRowsKeys,
        /**
         * Drags an element over another element.
         * @param {number} sourceIndex
         * @param {number} targetIndex
         * @param [options]
         * @param {number} [options.steps] number of movement steps (only for puppeteer)
         */
        dragOver: async (sourceIndex, targetIndex, options = {}) => getDragAndDrop().dragOver(getRowByIndex(sourceIndex), getRowByIndex(targetIndex), options),
        dragStart: async (index) => getDragAndDrop().dragStart(getRowByIndex(index)),
        dragMove: () => getDragAndDrop().dragMove(),
        dragEnd: async (index) => getDragAndDrop().dragEnd(getRowByIndex(index)),
        _getDragAndDropDriver: getDragAndDrop,
        getNavigationModal,
        getCustomFieldModal,
        getArchiveFieldModal,
        getManageTagsModal,
        getTagsAssignButton,
        getInternalScrollElement: () => base.$('[data-hook="toolbar-collection-content-auto-sizer"]'),
        internalScrollDownToItem: (index, options = {}) => scrollDownToItem(index, {
            ...options,
            scrollableElementDataHook: 'toolbar-collection-content-auto-sizer',
        }),
        getSectionHeadersCount: () => base.$$('[data-hook="table-section-header"]').count(),
        getSectionHeaderAt: (index) => CollectionSectionHeaderUniDriver(base.$$('[data-hook="table-section-header"]').get(index), body),
        /** Get column indices that are currently searchable */
        getSearchableColumns: async () => {
            const searchableColumnIndices = [];
            // Get all data rows and check the first one
            const rows = base.$$('tbody tr');
            const rowCount = await rows.count();
            if (rowCount === 0) {
                return searchableColumnIndices;
            }
            const firstRow = rows.get(0);
            const cells = firstRow.$$('td');
            const cellCount = await cells.count();
            // Check each cell to see if it contains SearchableCollectionContent
            for (let columnIndex = 0; columnIndex < cellCount; columnIndex++) {
                const cell = cells.get(columnIndex);
                const searchableContent = cell.$('[data-hook="searchable-collection-content"]');
                if (await searchableContent.exists()) {
                    searchableColumnIndices.push(columnIndex);
                }
            }
            return searchableColumnIndices;
        },
    };
}
export default TableUniDriver;
//# sourceMappingURL=Table.uni.driver.js.map