import { UniDriver, waitFor } from '@wix/wix-ui-test-utils/unidriver';
import {
  ButtonUniDriver,
  CounterBadgeUniDriver,
  InputWithOptionsUniDriver,
  SearchUniDriver,
  TextButtonUniDriver,
} from '@wix/design-system/dist/testkit/unidriver';
import { CollectionDropdownFilterUniDriver } from '../DropdownFilter/CollectionDropdownFilter.uni.driver';
import { FiltersPanelUniDriver } from '../FiltersPanel/FiltersPanel.uni.driver';
import { CollectionViewsDropdownUniDriver } from '../CollectionViewsDropdown/CollectionViewsDropdown.uni.driver';
import TabsFilterUniDriver from '../TabsFilter/TabsFilter.uni.driver';
import { TableGridSwitchButtonUniDriver } from '../TableGridSwitchButton/TableGridSwitchButton.uni.driver';
import { CustomColumnsPanelUniDriver } from '../CustomColumnsPanel/CustomColumnsPanel.uni.driver';
import { MultiLevelSortingPanelUniDriver } from '../MultiLevelSortingPanel/MultiLevelSortingPanel.uni.driver';
import { baseUniDriverFactory } from '../../unidriver';
import { MultiLevelSortingCTAButtonUniDriver } from '../MultiLevelSorting/MultiLevelSortingCTAButton.uni.driver';
import { ToolbarSecondaryActionsUniDriver } from './ToolbarSecondaryActions.uni.driver';
import { MoreActionsUniDriver } from '../MoreActions/MoreActions.uni.driver';

export const CollectionToolbarUniDriver = (
  base: UniDriver,
  body: UniDriver,
) => {
  const toolbar = baseUniDriverFactory(base);

  /** Returns more actions button driver */
  const getMoreActions = (dataHook: string = 'toolbar-more-actions') =>
    MoreActionsUniDriver(base, body).getMoreActions(dataHook);

  const getSecondaryActions = (index: number, dataHook?: string) =>
    ToolbarSecondaryActionsUniDriver(base, body).getSecondaryActions(
      dataHook,
      index,
    );

  const searchDriver = {
    ...SearchUniDriver(base.$('[data-hook="collection-search"]'), body),
    click: () => base.$('[data-hook="collection-search"]').click(),
  };

  const filtersPanel = FiltersPanelUniDriver(body, body);

  const getSwitchButton = () =>
    TableGridSwitchButtonUniDriver(
      base.$('[data-hook="table-grid-switch-toggle-button"]'),
      body,
    );

  const getSwitchButtonDivider = () =>
    base.$('[data-hook="collection-table--grid-switch--divider"]');

  const getPrimaryActionButton = () =>
    ButtonUniDriver(
      base.$('[data-hook="toolbar-primary-action-button"]'),
      body,
    );

  const getInputWithOptionsDriver = (filterName: string) => {
    const dataHook = `collection-filter-${filterName}`;
    const inputWithOptionsBase = base.$(`[data-hook="${dataHook}"]`);
    const inputWithOptionsDriver = InputWithOptionsUniDriver(
      inputWithOptionsBase,
      body,
    );

    return CollectionDropdownFilterUniDriver(
      inputWithOptionsDriver,
      dataHook,
      inputWithOptionsBase,
      body,
    );
  };

  const isFiltersPanelOpen = () => filtersPanel.isOpen();

  const getMoreFiltersPanelButton = () =>
    ButtonUniDriver(base.$('[data-hook="more-filters-button"]'), body);

  const clickMoreFiltersButton = () => getMoreFiltersPanelButton().click();
  const closeFiltersPanel = () => filtersPanel.clickCloseButton();

  const getViewsDropdown = () => CollectionViewsDropdownUniDriver(base, body);

  const isToolbarWithFilters = async () =>
    base.$(`[data-hook="toolbar-item-filter-0"]`).exists();

  const getToolbarFiltersItemsCount = async () => {
    return base.$$(`[data-hook^="toolbar-item-filter-"]`).count();
  };

  const getToolbarFilterTitleAt = async (index: number) => {
    return base
      .$(
        `[data-hook="toolbar-item-filter-${index}"] [data-hook="table-toolbar-label"]`,
      )
      .text();
  };

  const getMultiLevelSortingButton = () =>
    MultiLevelSortingCTAButtonUniDriver(base, body, {
      dataHook: 'multi-level-sorting-button',
    });

  const slidingModalBase = () => body.$('[data-hook="cairo-side-panel-modal"]');

  const customColumnsPopover = () => CustomColumnsPanelUniDriver(body, body);

  const getMultiLevelSortingPanel = () =>
    MultiLevelSortingPanelUniDriver(body, body);

  const getSearchCancelButton = () =>
    TextButtonUniDriver(base.$('[data-hook="collection-search-cancel"]'), body);

  return {
    ...toolbar,
    getSearch: () => searchDriver,
    getToolbarFiltersItemsCount,
    getMoreActions,
    getSecondaryActions,
    wait: (timeout?: number) => base.wait(timeout),
    /** Get the text of the search input */
    getSearchText: searchDriver.inputDriver.getText,
    /** Enter text into the search input */
    enterSearchText: searchDriver.inputDriver.enterText,
    /** Get the max length of the search input */
    getSearchMaxLength: searchDriver.inputDriver.getMaxLength,
    /** Clear the text of the search input */
    clearSearchText: searchDriver.inputDriver.clearText,
    /** Indicates search input exists */
    searchInputExists: searchDriver.inputDriver.exists,
    /** Clicks clear button of the search input */
    clickClearSearchText: searchDriver.inputDriver.clickClear,
    /** Indicates whether the search cancel button exists */
    searchCancelButtonExists: () => getSearchCancelButton().exists(),
    /** Clicks the cancel button of the search */
    clickSearchCancelButton: () => getSearchCancelButton().click(),
    isToolbarWithFilters: () => isToolbarWithFilters(),
    skeletonExists: () => base.$('[data-hook="toolbar-skeleton"]').exists(),
    viewsSkeletonExists: () =>
      base.$('[data-hook="toolbar-views-skeleton"]').exists(),
    getViewsDropdown: () => getViewsDropdown(),
    openViewsDropdown: () => getViewsDropdown().open(),
    getViewListItemAt: (index: number) =>
      getViewsDropdown().getViewListItemAt(index),
    getViewListItemById: (id: string) =>
      getViewsDropdown().getViewListItemById(id),
    getSaveViewModal: () => getViewsDropdown().getSaveViewModal(),
    getRenameViewModal: () => getViewsDropdown().getRenameViewModal(),
    /** Click on a FiltersPanel's "clear" button */
    clickClearAllFilters: filtersPanel.clickClearAllFilters,
    /** Get text from FiltersPanel's "applied filter" section **/
    getFilterAppliedText: filtersPanel.getFilterAppliedText,
    /** get FiltersPanel's "clear" button */
    getClearAllFiltersButton: filtersPanel.getClearAllFiltersButton,
    /**
     * Select specific items of a dropdown filter that placed in the table toolbar
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param ids - keys of the items to select
     * @param options
     * @param options.closeDropdown - close the dropdown after selection - default=true
     */
    selectFilterOptions: (
      filterName: string,
      ids: string[],
      options?: { closeDropdown?: boolean },
    ) => {
      return getInputWithOptionsDriver(filterName).selectOptions(ids, options);
    },

    /**
     * Select specific items of a dropdown filter that placed in the table toolbar
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param id - keys of the item to select
     * @param options
     */
    selectFilterOption: (filterName: string, id: string) => {
      return getInputWithOptionsDriver(filterName).selectOption(id);
    },
    /**
     * Select random options of a dropdown filter that placed in the table toolbar
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param count - number of items to select
     * @param options
     * @param options.closeDropdown - close the dropdown after selection - default=true
     */
    selectFilterRandomOptions: (
      filterName: string,
      count: number,
      options?: { closeDropdown?: boolean },
    ) => {
      return getInputWithOptionsDriver(filterName).selectRandomOptions(
        count,
        options,
      );
    },
    /**
     * Returns the amount of available filters options in the dropdown
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getFilterDropdownOptionsCount: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).getOptionsCount();
    },
    /**
     * Returns all available filter's options ids
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getFilterDropdownAvailableOptionsIds: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).getAvailableOptionsIds();
    },
    /**
     * Returns filter's placeholder text
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getFilterInputPlaceholder: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).getInputPlaceholder();
    },
    /**
     * Indicates if the filter is in the empty state
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    isFilterInNoSearchResultsState: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).isNoSearchResults();
    },
    /**
     * Gets the empty state text
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getFilterEmptyStateText: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).getEmptyStateText();
    },
    /**
     * Indicates if the filter is in the empty state
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    isFilterInErrorState: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).isErrorStateExists();
    },
    /**
     * Indicates if the filter has this element. Can be used to check fixedFooter, for example
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param dataHook - the dataHook of the element
     */
    existsInFilter: async (filterName: string, dataHook: string) => {
      await getInputWithOptionsDriver(filterName).inputDriver.click();
      return getInputWithOptionsDriver(filterName).existInFilter(dataHook);
    },
    /**
     * Enters a text to the filter's Input
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param value
     */
    enterFilterText: async (filterName: string, value: string) => {
      await getInputWithOptionsDriver(filterName).inputDriver.click();
      return getInputWithOptionsDriver(filterName).inputDriver.enterText(value);
    },
    /**
     * Select specific items of a dropdown filter that placed in the filter panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param ids - keys of the items to select
     * @param options
     * @param options.closePanel - close the filters panel after selection - default=true
     * @param options.closeAccordionItem - close the accordion item after selection - default=true
     * @param options.closeDropdown - close the dropdown after selection - default=true
     */
    selectPanelFilterOptions: async (
      filterName: string,
      ids: string[],
      options?: {
        closePanel?: boolean;
        closeAccordionItem?: boolean;
        closeDropdown?: boolean;
      },
    ) => {
      if (!(await isFiltersPanelOpen())) {
        await clickMoreFiltersButton();
      }

      return filtersPanel.selectFilterOptions(filterName, ids, options);
    },
    /**
     * Select random items of a dropdown filter that placed in the table toolbar
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param count - number of items to select
     * @param options
     * @param options.closePanel - close the filters panel after selection - default=true
     * @param options.closeAccordionItem - close the accordion item after selection - default=true
     * @param options.closeDropdown - close the dropdown after selection - default=true
     */
    selectPanelFilterRandomOptions: async (
      filterName: string,
      count: number,
      options?: {
        closePanel?: boolean;
        closeAccordionItem?: boolean;
        closeDropdown?: boolean;
      },
    ) => {
      if (!(await isFiltersPanelOpen())) {
        await clickMoreFiltersButton();
      }

      return filtersPanel.selectFilterRandomOptions(filterName, count, options);
    },

    /**
     * Select specific items of a inline checkboxes filter that placed in the filter panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param ids - keys of the items to select
     * @param options
     * @param options.closePanel - close the filters panel after selection - default=true
     * @param options.closeAccordionItem - close the accordion item after selection - default=true
     */
    checkPanelFilterInlineCheckboxes: async (
      filterName: string,
      ids: string[],
      options: { closePanel?: boolean; closeAccordionItem?: boolean } = {},
    ) => {
      if (!(await isFiltersPanelOpen())) {
        await clickMoreFiltersButton();
      }

      return filtersPanel.checkFilterInlineCheckboxes(filterName, ids, options);
    },

    /**
     * Select specific/random dates in date range filter that placed in the filter panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param options.dates - specific dates to choose
     * @param options.closePanel - close the filters panel after selection - default=true
     * @param options.closeAccordionItem - close the accordion item after selection - default=true
     */
    choosePanelFilterDateRange: async (
      filterName: string,
      options: {
        closePanel?: boolean;
        closeAccordionItem?: boolean;
        dates?: Date[];
      } = {},
    ) => {
      if (!(await isFiltersPanelOpen())) {
        await clickMoreFiltersButton();
      }

      return filtersPanel.chooseDatesFilterDateRange(filterName, options);
    },
    /**
     * Open filter panel and click a filter with 'filterName'
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    clickFilterInPanel: async (filterName: string) => {
      if (!(await isFiltersPanelOpen())) {
        await clickMoreFiltersButton();
      }

      return filtersPanel.clickFilterTitle(filterName);
    },
    /** Indicates whether the filters panel is open */
    isFiltersPanelOpen: () => filtersPanel.isOpen(),
    /** Get layout switch button */
    getSwitchButton,
    /** get layout switch button divider */
    getSwitchButtonDivider,
    /** Get the title of the filters panel */
    getFiltersPanelTitleText: () => filtersPanel.getTitleText(),
    /** Opens the filters panel */
    clickMoreFiltersButton: () => clickMoreFiltersButton(),
    /** Returns the number of applied filters in the Filters badge */
    getMoreFiltersButtonCounterValue: () =>
      CounterBadgeUniDriver(
        base.$('[data-hook="more-filters-button-counter"]'),
        body,
      ).getContentText(),
    /** Closes the filters panel */
    closeFiltersPanel: () => closeFiltersPanel(),
    /** Closes custom columns panel */
    closeCustomColumnsPanel: () => customColumnsPopover().clickCloseButton(),
    getCustomColumnsPanelTitle: () => customColumnsPopover().getTitle(),
    getCustomColumnsPanelSubtitle: () => customColumnsPopover().getSubtitle(),
    customColumnsPanelCloseButtonExists: () =>
      customColumnsPopover().closeButtonExists(),
    getCustomColumnInfoIconAt: (index: number) =>
      customColumnsPopover().getCustomColumnInfoIconAt(index),
    /**
     * Gets the title of the accordion item of a filter placed in the filters panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getPanelFilterTitleText: (filterName: string) =>
      filtersPanel.getFilterTitleText(filterName),
    /**
     * Click on a title of the accordion item of a filter placed in the filters panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    clickPanelFilterTitle: (filterName: string) =>
      filtersPanel.clickFilterTitle(filterName),
    /**
     * Indicates whether the accordion item of filter placed in the filters panel is open
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    isPanelFilterExpanded: (filterName: string) =>
      filtersPanel.isFilterExpanded(filterName),
    /**
     * Get a testkit for a custom filter that places in the filters panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     * @param fn - a callback function that accepts the filters panel base element and should return a testkit that matches the custom filter
     */
    getPanelCustomFilter: async <E, T>(
      filterName: string,
      fn: (element: E) => T,
    ) => filtersPanel.getCustomFilter(filterName, fn),
    /**
     * Get a testkit for a custom filter that places in the filters panel
     * @param fn - a callback function that accepts the toolbar base element and should return a testkit that matches the custom filter
     */
    getCustomFilter: async <E, T>(fn: (element: E) => T) =>
      fn(await toolbar.element()),
    /** Indicates whether the button that open the filters panel exists */
    moreFilterButtonExists: () => getMoreFiltersPanelButton().exists(),
    /** Gets the title of the button that open the filters panel exists */
    getMoreFiltersButtonText: () =>
      getMoreFiltersPanelButton().getButtonTextContent(),

    /** Clicks the button that opens the custom columns dropdown */
    clickCustomColumnsButton: () =>
      base.$('[data-hook="custom-columns-button"]').click(),
    customColumnsButtonExists: () =>
      base.$('[data-hook="custom-columns-button"]').exists(),
    /** Indicates whether the custom columns dropdown is open */
    isCustomColumnsOpen: () => customColumnsPopover().isOpen(),
    isCustomColumnsAddNewExist: () => customColumnsPopover().addNewExists(),
    clickCustomColumnsAddNew: () => customColumnsPopover().clickAddNew(),
    waitSidePanelFullyOpened: () => {
      return waitFor(
        async () =>
          (await slidingModalBase().attr('data-transition-status')) ===
          'entered',
      );
    },
    waitSidePanelFullyClosed: () => {
      return waitFor(
        async () =>
          (await slidingModalBase().attr('data-transition-status')) ===
          'exited',
      );
    },
    /**
     * Click checkboxes that hide / show columns from the table
     * @param ids - IDs of the columns to click on
     */
    clickCustomColumnsCheckboxes: (ids: string[]) =>
      customColumnsPopover().checkOptions(ids),
    /**
     * Get a column checkbox within the custom columns dropdown
     * @param index - index of the column checkbox
     */
    getCustomColumnCheckboxAt: (index: number) =>
      customColumnsPopover().getCheckboxAt(index),
    /**
     * Get full custom columns driver
     */
    getCustomColumnsDriver: () => customColumnsPopover(),
    /**
     * Get a column checkboxes count within the custom columns dropdown
     */
    getCustomColumnCheckboxesCount: () =>
      customColumnsPopover().getCheckboxesCount(),
    /**
     * Get a column checkbox within the custom columns dropdown
     * @param id - column id
     */
    getCustomColumnCheckboxById: (id: string) =>
      customColumnsPopover().getCheckboxById(id),
    /**
     * Get all custom columns items text
     */
    getCustomColumnsTitles: () => customColumnsPopover().getTitles(),
    /** Returns the amount of hidden columns rendered in the CustomColumns CTA badge */
    getCustomColumnsButtonCounterValue: () =>
      CounterBadgeUniDriver(
        base.$('[data-hook="custom-columns-button-counter"]'),
        body,
      ).getContentText(),
    /**
     * Get a InputWithOptions testkit for a filter that placed in the table toolbar
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getInputWithOptions: (filterName: string) =>
      getInputWithOptionsDriver(filterName),
    /**
     * Get a InputWithOptions testkit for a filter that placed in the filters panel
     * @param filterName - the key of the filter in the filters object passed to `useCollection`
     */
    getFiltersPanelInputWithOptions: (filterName: string) =>
      filtersPanel.getInputWithOptionsDriver(filterName),
    getFiltersPanelMultiSelect: (filterName: string) =>
      filtersPanel.getFiltersPanelMultiSelect(filterName),
    /**
     * Scrolls down a dropdown filter placed in the table toolbar
     * @param filterName
     */
    scrollDownFilter: (filterName: string) =>
      getInputWithOptionsDriver(filterName).scrollDown(),

    /**
     * Scrolls down a dropdown filter placed in the filters panel
     * @param filterName
     */
    scrollDownPanelFilter: (filterName: string) =>
      filtersPanel.getInputWithOptionsDriver(filterName).scrollDown(),

    clickFilterClearButton: (filterName: string) => {
      return getInputWithOptionsDriver(filterName).clickInputClear();
    },

    /**
     * Returns an amount of filters currently rendered in the Panel.
     */
    getPanelFiltersCount: filtersPanel.getPanelFiltersCount,

    /**
     * Returns filter's title at in at index.
     */
    /**
     * Get a filter's title
     * @param index - index of the filter
     */
    getToolbarFilterTitleAt,

    /**
     * Get tabs driver
     */
    getTabs: () =>
      TabsFilterUniDriver(base.$('[data-hook="collection-tabs-filter"]'), body),

    /**
     * Get date range filter by filter name
     */
    getDateRangeFilter: filtersPanel.getDateRangeFilter,

    /**
     * Get number range filter driver by filter name
     */
    getNumberRangeFilterDriver: (filterName: string) =>
      filtersPanel.getNumberRangeDriver(filterName),

    /**
     * Get operator picker filter driver by filter name
     */
    getOperatorPickerDriver: (filterName: string) =>
      filtersPanel.getOperatorPickerDriver(filterName),

    /**
     * Get radio group filter by filter name
     */
    getRadioGroupFilter: filtersPanel.getRadioGroupFilter,

    getMultiLevelSortingButton,

    /**
     * Checks if the MultiLevelSorting button exists in the toolbar
     */
    multiLevelSortingButtonExists: () =>
      getMultiLevelSortingButton().getIconButton().exists(),

    /**
     * Clicks the MultiLevelSorting button
     */
    clickMultiLevelSortingButton: () =>
      getMultiLevelSortingButton().getIconButton().click(),

    getMultiLevelSortingButtonCounterValue: () =>
      CounterBadgeUniDriver(
        base.$('[data-hook="multi-level-sorting-button-counter"]'),
        body,
      ).getContentText(),

    /**
     * MultiLevelSorting panel driver
     */
    getMultiLevelSortingPanel,
    /**
     * Button driver for primary action button
     */
    getPrimaryActionButton,

    getMainToolbar: () => base.$('[data-hook="collection-table-toolbar"]'),

    /**
     * Indicates whether the collection table toolbr total items counter exists
     */
    isShowTotal: () => base.$('[data-hook="toolbar-counter"]').exists(),

    /**
     * Gets collection table toolbr total items counter
     */
    getShowTotal: () =>
      CounterBadgeUniDriver(
        base.$('[data-hook="toolbar-counter"]'),
        body,
      ).getContentText(),

    /**
     * Indicates whether the collection table title exists
     */
    isTitleExists: () => base.$('[data-hook="toolbar-title"]').exists(),

    /**
     * Get collection table toolbar title
     */
    getTitleText: () => base.$('[data-hook="toolbar-title"]').text(),

    /**
     * Get collection table toolbar subtitle
     */
    getSubtitleText: () => base.$('[data-hook="toolbar-subtitle"]').text(),

    /**
     * Get collection table toolbar learn more button
     */
    getSubtitleLearnMoreButton: () =>
      TextButtonUniDriver(
        base.$('[data-hook="toolbar-subtitle-learn-more-cta"]'),
        body,
      ),
    applyButton: () => filtersPanel.applyButton(),
    discardChangesModal: () => filtersPanel.discardChangesModal(),
    getFiltersPanelItemTextAt: filtersPanel.getItemTextAt,
    getFiltersPanelItemsCount: filtersPanel.getItemsCount,
    getCustomFieldOptionsAt: (index: number) =>
      customColumnsPopover().getCustomFieldOptionsAt(index),
    _filtersPanel: () => filtersPanel,
  };
};
