import { UniDriver } from '@wix/wix-ui-test-utils/unidriver';
import { SlidingModalUniDriver } from '../SlidingModal/SlidingModal.uni.driver';
import {
  AccordionUniDriver,
  ButtonUniDriver,
  DropdownBaseUniDriver,
  FieldSetUniDriver,
  FormFieldUniDriver,
  InputUniDriver,
  InputWithOptionsUniDriver,
  MessageModalLayoutUniDriver,
  MultiSelectUniDriver,
  SidePanelUniDriver,
  TextButtonUniDriver,
  TextUniDriver,
} from '@wix/design-system/dist/testkit/unidriver';
import { CollectionDropdownFilterUniDriver } from '../DropdownFilter/CollectionDropdownFilter.uni.driver';
import { MultiInlineCheckboxUniDriver } from '../MultiInlineCheckbox/MultiInlineCheckbox.uni.driver';
import { DateRangePickerUniDriver } from '../DateRangePicker/DateRangeFilter.uni.driver';
import { RadioGroupFilterUniDriver } from '../RadioGroupFilter/RadioGroupFilter.uni.driver';

export const FiltersPanelUniDriver = (base: UniDriver, body: UniDriver) => {
  const slidingModal = SlidingModalUniDriver(
    base.$('[data-hook="cairo-side-panel-modal"]'),
    body,
  );

  const sidePanel = SidePanelUniDriver(
    base.$('[data-hook="filters-side-panel"]'),
    body,
  );

  const accordionBase = base.$('[data-hook="filters-panel-accordion"]');

  const accordion = AccordionUniDriver(accordionBase, body);

  const getInlineCheckboxFilter = (filterName: string) =>
    MultiInlineCheckboxUniDriver(
      accordionBase.$(`[data-hook="collection-filter-${filterName}"]`),
      body,
    );

  const getDateRangeFilter = (filterName: string) =>
    DateRangePickerUniDriver(
      accordionBase.$(`[data-hook="date-range-filter-${filterName}"]`),
      body,
    );

  const getRadioGroupFilter = (filterName: string) =>
    RadioGroupFilterUniDriver(
      accordionBase.$(`[data-hook="radio-group-filter-${filterName}"]`),
      body,
    );

  const getAccordionItemTitle = (filterName: string) =>
    accordionBase.$(`[data-hook="accordion-item-title-${filterName}"]`);

  const getAccordionItem = async (filterName: string) => {
    const items = accordionBase.$$('[data-hook="accordion-item"]');

    const count = await items.count();
    for (let i = 0; i < count; i++) {
      const item = items.get(i);
      if (
        await item
          .$(`[data-hook="accordion-item-title-${filterName}"]`)
          .exists()
      ) {
        return item;
      }
    }

    throw new Error(`Accordion item for filter: "${filterName}" was not found`);
  };

  const getAccordionItemAt = (index: number) =>
    accordionBase.$$('[data-hook="accordion-item"]').get(index);

  const isFilterExpanded = async (filterName: string) =>
    (await getAccordionItem(filterName)).$('[data-hook="children"]').exists();

  const isFilterExpandedAt = (index: number) =>
    getAccordionItemAt(index).$('[data-hook="children"]').exists();

  const clickFilter = async (filterName: string) => {
    if (!(await isFilterExpanded(filterName))) {
      await clickFilterTitle(filterName);
    }
  };
  const checkFilterInlineCheckboxes = async (
    filterName: string,
    ids: string[],
    {
      closePanel = true,
      closeAccordionItem = true,
    }: { closePanel?: boolean; closeAccordionItem?: boolean } = {},
  ) => {
    await clickFilter(filterName);
    await getInlineCheckboxFilter(filterName).checkOptions(ids);

    if (closeAccordionItem) {
      await clickFilterTitle(filterName);
    }

    if (closePanel) {
      await sidePanel.clickClose();
    }
  };

  const chooseDatesFilterDateRange = async (
    filterName: string,
    {
      dates = undefined,
      closePanel = true,
      closeAccordionItem = true,
    }: {
      closePanel?: boolean;
      closeAccordionItem?: boolean;
      dates?: Date[];
    } = {},
  ) => {
    await clickFilter(filterName);
    const dateRange = await getDateRangeFilter(filterName);
    let datesResult;
    if (dates && dates.length > 1) {
      const from = await dateRange.chooseDate('from', dates[0]);
      const to = await dateRange.chooseDate('to', dates[1]);
      datesResult = { from, to };
    } else {
      datesResult = await dateRange.chooseRandomDates();
    }

    if (closeAccordionItem) {
      await clickFilterTitle(filterName);
    }

    if (closePanel) {
      await sidePanel.clickClose();
    }
    return datesResult;
  };

  const getInputWithOptionsDriver = (filterName: string) => {
    const dataHook = `collection-filter-${filterName}`;

    const optionsBase = accordionBase.$(
      `[data-hook="collection-filter-${filterName}"]`,
    );

    const inputWithOptionsDriver = InputWithOptionsUniDriver(optionsBase, body);

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

  const selectFilterOptions = async (
    filterName: string,
    ids: string[],
    {
      closePanel = true,
      closeAccordionItem = true,
      closeDropdown = true,
    }: {
      closePanel?: boolean;
      closeAccordionItem?: boolean;
      closeDropdown?: boolean;
    } = {},
  ) => {
    if (!(await isFilterExpanded(filterName))) {
      await clickFilterTitle(filterName);
    }

    await getInputWithOptionsDriver(filterName).selectOptions(ids, {
      closeDropdown,
    });

    if (closeAccordionItem) {
      await clickFilterTitle(filterName);
    }

    if (closePanel) {
      await sidePanel.clickClose();
    }
  };

  const selectFilterRandomOptions = async (
    filterName: string,
    count: number,
    {
      closePanel = true,
      closeAccordionItem = true,
      closeDropdown = true,
    }: {
      closePanel?: boolean;
      closeAccordionItem?: boolean;
      closeDropdown?: boolean;
    } = {},
  ) => {
    if (!(await isFilterExpanded(filterName))) {
      await clickFilterTitle(filterName);
    }

    const ids = await getInputWithOptionsDriver(filterName).selectRandomOptions(
      count,
      {
        closeDropdown,
      },
    );

    if (closeAccordionItem) {
      await getAccordionItemTitle(filterName).click();
    }

    if (closePanel) {
      await sidePanel.clickClose();
    }

    return ids;
  };

  const getFilterTitleText = (filterName: string) =>
    TextUniDriver(getAccordionItemTitle(filterName), body).getText();

  const getFilterAppliedText = () =>
    TextUniDriver(body.$('[data-hook="applied-filters"]'), body).getText();

  const clickFilterTitle = (filterName: string) =>
    getAccordionItemTitle(filterName).click();

  const getCustomFilter = async <E, T>(
    filterName: string,
    fn: (element: E) => T,
  ) => {
    if (!(await isFilterExpanded(filterName))) {
      await clickFilterTitle(filterName);
    }

    return fn(await accordion.element());
  };

  const clickClearAllFilters = () => getClearAllFiltersButton().click();

  const getClearAllFiltersButton = () =>
    TextButtonUniDriver(base.$('[data-hook="clear-all-filters"]'), body);

  const getPanelFiltersCount = () => {
    const items = accordionBase.$$('[data-hook="accordion-item"]');
    return items.count();
  };

  const getItemTextAt = (index: number) => {
    return accordionBase.$$(':scope > div').get(index).text();
  };

  const getItemsCount = () => {
    return accordionBase.$$(':scope > div').count();
  };

  return {
    isOpen: slidingModal.isOpen,
    clickCloseButton: async () => {
      if (await sidePanel.isCloseButtonExists()) {
        return sidePanel.clickClose();
      }
      if (await sidePanel.isBackButtonExists()) {
        return sidePanel.clickBack();
      }
    },
    getTitleText: sidePanel.getTitleText,
    selectFilterOptions,
    selectFilterRandomOptions,
    checkFilterInlineCheckboxes,
    chooseDatesFilterDateRange,
    getFilterTitleText,
    getFilterAppliedText,
    clickFilterTitle,
    isFilterExpanded,
    isFilterExpandedAt,
    getCustomFilter,
    getInputWithOptionsDriver,
    clickClearAllFilters,
    getClearAllFiltersButton,
    getPanelFiltersCount,
    getDateRangeFilter,

    getNumberRangeDriver: (filterName: string) => {
      const getNumberRangeFilter = () =>
        accordionBase.$(`[data-hook="number-range-filter-${filterName}"]`);

      const getNumberRangeFilterFromInput = () =>
        InputUniDriver(
          getNumberRangeFilter().$(
            '[data-hook="number-range-filter-from-input"]',
          ),
          body,
        );

      const getNumberRangeFilterToInput = () =>
        InputUniDriver(
          getNumberRangeFilter().$(
            '[data-hook="number-range-filter-to-input"]',
          ),
          body,
        );

      const getNumberRangeFilterFromError = () => {
        const fieldsetDriver = FieldSetUniDriver(getNumberRangeFilter(), body);
        return {
          exists: () => fieldsetDriver.hasStatus('error'),
          getText: () => fieldsetDriver.getStatusMessage(),
        };
      };

      const getNumberRangeFilterToError = () => {
        const formField = FormFieldUniDriver(
          getNumberRangeFilter().$(
            '[data-hook="number-range-filter-to-form-field"]',
          ),
          body,
        );
        return {
          exists: () => formField.hasStatus('error'),
          getText: () => formField.getStatusMessage(),
        };
      };

      return {
        getNumberRangeFilter,
        getNumberRangeFilterFromInput,
        getNumberRangeFilterToInput,
        getNumberRangeFilterFromError,
        getNumberRangeFilterToError,
      };
    },
    getRadioGroupFilter,

    getOperatorPickerDriver: (filterName: string) => {
      const getOperatorPicker = () =>
        accordionBase.$(`[data-hook="operator-picker-${filterName}"]`);

      const getOperatorDropdown = () =>
        DropdownBaseUniDriver(
          getOperatorPicker().$('[data-hook="operator-picker-operator-dropdown"]'),
          body,
        );

      const getValueInput = () =>
        InputUniDriver(
          getOperatorPicker().$('[data-hook="operator-picker-value-input"]'),
          body,
        );

      return {
        exists: () => getOperatorPicker().exists(),
        getOperatorPicker,
        getOperatorDropdown,
        getValueInput,
        selectOperator: async (optionIndex: number) => {
          const dropdown = getOperatorDropdown();
          await dropdown.clickTargetElement('operator-picker-trigger');
          await dropdown.selectOption(optionIndex);
        },
        getSelectedOperator: async () => {
          const dropdown = getOperatorDropdown();
          return dropdown.getSelectedOptionId();
        },
        enterValue: async (value: string) => {
          const input = getValueInput();
          await input.enterText(value);
        },
        getValue: () => getValueInput().getText(),
        blur: () => getValueInput().blur(),
      };
    },

    getItemTextAt,
    getItemsCount,
    applyButton: () =>
      ButtonUniDriver(base.$('[data-hook="apply-button"]'), body),
    discardChangesModal: () =>
      MessageModalLayoutUniDriver(
        body.$('[data-hook="discard-changes-modal"]'),
        body,
      ),
    getFiltersPanelMultiSelect: (filterName: string) =>
      MultiSelectUniDriver(
        accordionBase.$(`[data-hook="collection-filter-${filterName}"]`),
        base,
      ),
    _getAccordionItemTitle: (
      filterName: string,
      { index = 0 }: { index?: number } = {},
    ) =>
      accordionBase
        .$$(`[data-hook="accordion-item-title-${filterName}"]`)
        .get(index),
    _getInlineCheckboxFilter: getInlineCheckboxFilter,
  };
};
