import React, { useRef, useReducer, useEffect, useCallback } from 'react';
import { CaretDownMinor, PlusMinor } from '@shopify/polaris-icons';
import { classNames } from '../../utilities/css.js';
import { useBreakpoints } from '../../utilities/breakpoints.js';
import { usePrevious } from '../../utilities/use-previous.js';
import { getVisibleAndHiddenTabIndices } from './utilities.js';
import styles from './AlphaTabs.scss.js';
import { Tab } from './components/Tab/Tab.js';
import { TabMeasurer } from './components/TabMeasurer/TabMeasurer.js';
import { Panel } from './components/Panel/Panel.js';
import { List } from './components/List/List.js';
import { CreateViewModal } from './components/CreateViewModal/CreateViewModal.js';
import { useI18n } from '../../utilities/i18n/hooks.js';
import { Text } from '../Text/Text.js';
import { Icon } from '../Icon/Icon.js';
import { UnstyledButton } from '../UnstyledButton/UnstyledButton.js';
import { Box } from '../Box/Box.js';
import { Popover } from '../Popover/Popover.js';
import { Tooltip } from '../Tooltip/Tooltip.js';

const CREATE_NEW_VIEW_ID = 'create-new-view';
const AlphaTabs = ({
  tabs,
  children,
  selected,
  newViewAccessibilityLabel,
  canCreateNewView,
  disabled,
  onCreateNewView,
  onSelect,
  fitted,
  disclosureText
}) => {
  const i18n = useI18n();
  const {
    mdDown
  } = useBreakpoints();
  const scrollRef = useRef(null);
  const wrapRef = useRef(null);
  const selectedTabRef = useRef(null);
  const [state, setState] = useReducer((data, partialData) => {
    return { ...data,
      ...partialData
    };
  }, {
    disclosureWidth: 0,
    containerWidth: Infinity,
    tabWidths: [],
    visibleTabs: [],
    hiddenTabs: [],
    showDisclosure: false,
    tabToFocus: -1,
    isNewViewModalActive: false,
    modalSubmitted: false,
    isTabsFocused: false,
    isTabPopoverOpen: false,
    isTabModalOpen: false
  });
  const {
    tabToFocus,
    visibleTabs,
    hiddenTabs,
    showDisclosure,
    isNewViewModalActive,
    modalSubmitted,
    disclosureWidth,
    tabWidths,
    containerWidth,
    isTabsFocused,
    isTabModalOpen,
    isTabPopoverOpen
  } = state;
  const prevModalOpen = usePrevious(isTabModalOpen);
  const prevPopoverOpen = usePrevious(isTabPopoverOpen);
  useEffect(() => {
    const hasModalClosed = prevModalOpen && !isTabModalOpen;
    const hasPopoverClosed = prevPopoverOpen && !isTabPopoverOpen;

    if (hasModalClosed) {
      setState({
        isTabsFocused: true,
        tabToFocus: selected
      });
    } else if (hasPopoverClosed && !isTabModalOpen) {
      setState({
        isTabsFocused: true,
        tabToFocus: selected
      });
    }
  }, [prevPopoverOpen, isTabPopoverOpen, prevModalOpen, isTabModalOpen, selected, tabToFocus]);
  const handleTogglePopover = useCallback(isOpen => setState({
    isTabPopoverOpen: isOpen
  }), []);
  const handleToggleModal = useCallback(isOpen => setState({
    isTabModalOpen: isOpen
  }), []);

  const handleCloseNewViewModal = () => {
    setState({
      isNewViewModalActive: false
    });
  };

  const handleSaveNewViewModal = async value => {
    if (!onCreateNewView) {
      return false;
    }

    const hasExecuted = await (onCreateNewView === null || onCreateNewView === void 0 ? void 0 : onCreateNewView(value));

    if (hasExecuted) {
      setState({
        modalSubmitted: true
      });
    }

    return hasExecuted;
  };

  const handleClickNewTab = () => {
    setState({
      isNewViewModalActive: true
    });
  };

  const handleTabClick = useCallback(id => {
    const tab = tabs.find(aTab => aTab.id === id);

    if (tab == null) {
      return null;
    }

    const selectedIndex = tabs.indexOf(tab);
    onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedIndex);
  }, [tabs, onSelect]);
  const renderTabMarkup = useCallback((tab, index) => {
    const handleClick = () => {
      var _tab$onAction;

      handleTabClick(tab.id);
      (_tab$onAction = tab.onAction) === null || _tab$onAction === void 0 ? void 0 : _tab$onAction.call(tab);
    };

    const viewNames = tabs.map(({
      content
    }) => content);
    const tabPanelID = tab.panelID || `${tab.id}-panel`;
    return /*#__PURE__*/React.createElement(Tab, Object.assign({}, tab, {
      key: `${index}-${tab.id}`,
      id: tab.id,
      panelID: children ? tabPanelID : undefined,
      disabled: disabled,
      siblingTabHasFocus: tabToFocus > -1,
      focused: index === tabToFocus,
      selected: index === selected,
      onAction: handleClick,
      accessibilityLabel: tab.accessibilityLabel,
      url: tab.url,
      content: tab.content,
      onToggleModal: handleToggleModal,
      onTogglePopover: handleTogglePopover,
      viewNames: viewNames,
      ref: index === selected ? selectedTabRef : null
    }));
  }, [disabled, handleTabClick, tabs, children, selected, tabToFocus, handleToggleModal, handleTogglePopover]);
  const handleFocus = useCallback(event => {
    const target = event.target;
    const isItem = target.classList.contains(styles.Item);
    const isInNaturalDOMOrder = target.closest(`[data-tabs-focus-catchment]`) || isItem;
    const isDisclosureActivator = target.classList.contains(styles.DisclosureActivator);

    if (isDisclosureActivator || !isInNaturalDOMOrder) {
      return;
    }

    setState({
      isTabsFocused: true
    });
  }, []);
  const handleBlur = useCallback(event => {
    var _relatedTarget$closes, _target$classList, _target$classList$con;

    const target = event.target;
    const relatedTarget = event.relatedTarget;
    const isInNaturalDOMOrder = relatedTarget === null || relatedTarget === void 0 ? void 0 : (_relatedTarget$closes = relatedTarget.closest) === null || _relatedTarget$closes === void 0 ? void 0 : _relatedTarget$closes.call(relatedTarget, `.${styles.AlphaTabs}`);
    const targetIsATab = target === null || target === void 0 ? void 0 : (_target$classList = target.classList) === null || _target$classList === void 0 ? void 0 : (_target$classList$con = _target$classList.contains) === null || _target$classList$con === void 0 ? void 0 : _target$classList$con.call(_target$classList, styles.Tab);
    const focusReceiverIsAnItem = relatedTarget === null || relatedTarget === void 0 ? void 0 : relatedTarget.classList.contains(styles.Item);

    if (!relatedTarget && !isTabModalOpen && !targetIsATab && !focusReceiverIsAnItem) {
      setState({
        tabToFocus: -1
      });
      return;
    }

    if (!isInNaturalDOMOrder && !isTabModalOpen && !targetIsATab && !focusReceiverIsAnItem) {
      setState({
        tabToFocus: -1
      });
      return;
    }

    setState({
      isTabsFocused: false
    });
  }, [isTabModalOpen]);

  const handleKeyDown = event => {
    if (isTabPopoverOpen || isTabModalOpen || isNewViewModalActive) {
      return;
    }

    const {
      key
    } = event;

    if (key === 'ArrowLeft' || key === 'ArrowRight') {
      event.preventDefault();
      event.stopPropagation();
    }
  };

  useEffect(() => {
    const {
      visibleTabs,
      hiddenTabs
    } = getVisibleAndHiddenTabIndices(tabs, selected, disclosureWidth, tabWidths, containerWidth);
    setState({
      visibleTabs,
      hiddenTabs
    });
  }, [containerWidth, disclosureWidth, tabs, selected, tabWidths, setState]);
  const moveToSelectedTab = useCallback(() => {
    var _selectedTabRef$curre;

    const activeButton = (_selectedTabRef$curre = selectedTabRef.current) === null || _selectedTabRef$curre === void 0 ? void 0 : _selectedTabRef$curre.querySelector(`.${styles['Tab-active']}`);

    if (activeButton) {
      moveToActiveTab(activeButton.offsetLeft);
    }
  }, []);
  useEffect(() => {
    if (mdDown) {
      moveToSelectedTab();
    }
  }, [moveToSelectedTab, selected, mdDown]);
  useEffect(() => {
    if (isTabsFocused && !showDisclosure) {
      const tabToFocus = selected;
      setState({
        tabToFocus
      });
    }
  }, [isTabsFocused, selected, setState, showDisclosure]);

  const handleKeyPress = event => {
    const {
      showDisclosure,
      visibleTabs,
      hiddenTabs,
      tabToFocus,
      isNewViewModalActive
    } = state;

    if (isTabModalOpen || isTabPopoverOpen || isNewViewModalActive) {
      return;
    }

    const key = event.key;
    const tabsArrayInOrder = showDisclosure || mdDown ? visibleTabs.concat(hiddenTabs) : [...visibleTabs];
    let newFocus = tabsArrayInOrder.indexOf(tabToFocus);

    if (key === 'ArrowRight') {
      newFocus += 1;

      if (newFocus === tabsArrayInOrder.length) {
        newFocus = 0;
      }
    }

    if (key === 'ArrowLeft') {
      if (newFocus === -1 || newFocus === 0) {
        newFocus = tabsArrayInOrder.length - 1;
      } else {
        newFocus -= 1;
      }
    }

    const buttonToFocus = tabsArrayInOrder[newFocus];

    if (buttonToFocus != null) {
      setState({
        tabToFocus: buttonToFocus
      });
    }
  };

  const handleDisclosureActivatorClick = () => {
    setState({
      showDisclosure: !showDisclosure,
      tabToFocus: hiddenTabs[0]
    });
  };

  const handleClose = () => {
    setState({
      showDisclosure: false
    });
  };

  const handleMeasurement = useCallback(measurements => {
    const {
      hiddenTabWidths: tabWidths,
      containerWidth,
      disclosureWidth
    } = measurements;
    const {
      visibleTabs,
      hiddenTabs
    } = getVisibleAndHiddenTabIndices(tabs, selected, disclosureWidth, tabWidths, containerWidth);
    setState({
      visibleTabs,
      hiddenTabs,
      disclosureWidth,
      containerWidth,
      tabWidths
    });
  }, [tabs, selected, setState]);

  const handleListTabClick = id => {
    handleTabClick(id);
    handleClose();
    setState({
      isTabsFocused: true
    });
  };

  const moveToActiveTab = offsetLeft => {
    setTimeout(() => {
      if (scrollRef.current && typeof scrollRef.current.scroll === 'function') {
        var _wrapRef$current, _scrollRef$current;

        const scrollRefOffset = (wrapRef === null || wrapRef === void 0 ? void 0 : (_wrapRef$current = wrapRef.current) === null || _wrapRef$current === void 0 ? void 0 : _wrapRef$current.offsetLeft) || 0;
        scrollRef === null || scrollRef === void 0 ? void 0 : (_scrollRef$current = scrollRef.current) === null || _scrollRef$current === void 0 ? void 0 : _scrollRef$current.scroll({
          left: offsetLeft - scrollRefOffset
        });
      }
    }, 0);
  };

  const createViewA11yLabel = newViewAccessibilityLabel || i18n.translate('Polaris.Tabs.newViewAccessibilityLabel');
  const tabsToShow = mdDown ? [...visibleTabs, ...hiddenTabs] : visibleTabs;
  const tabsMarkup = tabsToShow.sort((tabA, tabB) => tabA - tabB).filter(tabIndex => tabs[tabIndex]).map(tabIndex => renderTabMarkup(tabs[tabIndex], tabIndex));
  const disclosureActivatorVisible = visibleTabs.length < tabs.length && !mdDown;
  const classname = classNames(styles.AlphaTabs, fitted && styles.fitted, disclosureActivatorVisible && styles.fillSpace);
  const wrapperClassNames = classNames(styles.Wrapper, canCreateNewView && styles.WrapperWithNewButton);
  const disclosureTabClassName = classNames(styles.DisclosureTab, disclosureActivatorVisible && styles['DisclosureTab-visible']);
  const disclosureButtonClassName = classNames(styles.DisclosureActivator);
  const disclosureButtonContent = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
    as: "span",
    variant: "bodySm",
    fontWeight: "semibold"
  }, disclosureText !== null && disclosureText !== void 0 ? disclosureText : i18n.translate('Polaris.Tabs.toggleTabsLabel')), /*#__PURE__*/React.createElement("div", {
    className: classNames(styles.IconWrap, disclosureActivatorVisible && showDisclosure && styles['IconWrap-open'])
  }, /*#__PURE__*/React.createElement(Icon, {
    source: CaretDownMinor,
    color: "subdued"
  })));
  const disclosureButton = /*#__PURE__*/React.createElement(UnstyledButton, {
    type: "button",
    className: disclosureButtonClassName,
    onClick: handleDisclosureActivatorClick,
    "aria-label": disclosureText !== null && disclosureText !== void 0 ? disclosureText : i18n.translate('Polaris.Tabs.toggleTabsLabel'),
    disabled: disabled
  }, disclosureButtonContent);
  const activator = disclosureButton;
  const disclosureTabs = hiddenTabs.map(tabIndex => tabs[tabIndex]);
  const viewNames = tabs.map(({
    content
  }) => content);
  const tabMeasurer = /*#__PURE__*/React.createElement(TabMeasurer, {
    tabToFocus: tabToFocus,
    activator: activator,
    selected: selected,
    tabs: tabs,
    siblingTabHasFocus: tabToFocus > -1,
    handleMeasurement: handleMeasurement
  });
  const newTab = /*#__PURE__*/React.createElement(Tab, {
    id: CREATE_NEW_VIEW_ID,
    content: createViewA11yLabel,
    actions: [],
    onAction: handleClickNewTab,
    onFocus: () => {
      if (modalSubmitted) {
        setState({
          tabToFocus: selected,
          modalSubmitted: false
        });
      }
    },
    icon: /*#__PURE__*/React.createElement(Icon, {
      source: PlusMinor,
      accessibilityLabel: createViewA11yLabel
    }),
    disabled: disabled,
    onTogglePopover: handleTogglePopover,
    onToggleModal: handleToggleModal,
    tabIndexOverride: 0
  });
  const panelMarkup = children ? tabs.map((_tab, index) => {
    return selected === index ? /*#__PURE__*/React.createElement(Panel, {
      id: tabs[index].panelID || `${tabs[index].id}-panel`,
      tabID: tabs[index].id,
      key: tabs[index].id
    }, children) : /*#__PURE__*/React.createElement(Panel, {
      id: tabs[index].panelID || `${tabs[index].id}-panel`,
      tabID: tabs[index].id,
      key: tabs[index].id,
      hidden: true
    });
  }) : null;
  return /*#__PURE__*/React.createElement("div", {
    className: styles.Outer
  }, /*#__PURE__*/React.createElement(Box, {
    padding: {
      md: '2'
    },
    background: "bg",
    borderColor: "border-subdued",
    borderBlockEndWidth: "1",
    borderRadiusStartEnd: "2",
    borderRadiusStartStart: "2"
  }, tabMeasurer, /*#__PURE__*/React.createElement("div", {
    className: wrapperClassNames,
    ref: scrollRef
  }, /*#__PURE__*/React.createElement("div", {
    className: styles.ButtonWrapper,
    ref: wrapRef
  }, /*#__PURE__*/React.createElement("ul", {
    role: tabsMarkup.length > 0 ? 'tablist' : undefined,
    className: classname,
    onFocus: handleFocus,
    onBlur: handleBlur,
    onKeyDown: handleKeyDown,
    onKeyUp: handleKeyPress,
    "data-tabs-focus-catchment": true
  }, tabsMarkup, mdDown || tabsToShow.length === 0 ? null : /*#__PURE__*/React.createElement("li", {
    className: disclosureTabClassName,
    role: "presentation"
  }, /*#__PURE__*/React.createElement(Popover, {
    preferredPosition: "below",
    activator: activator,
    active: disclosureActivatorVisible && showDisclosure,
    onClose: handleClose,
    autofocusTarget: "first-node"
  }, /*#__PURE__*/React.createElement(List, {
    focusIndex: hiddenTabs.indexOf(tabToFocus),
    disclosureTabs: disclosureTabs,
    onClick: handleListTabClick,
    onKeyPress: handleKeyPress
  })))), canCreateNewView && tabsToShow.length > 0 ? /*#__PURE__*/React.createElement("div", {
    className: styles.NewTab
  }, /*#__PURE__*/React.createElement(CreateViewModal, {
    open: isNewViewModalActive,
    onClose: handleCloseNewViewModal,
    onClickPrimaryAction: handleSaveNewViewModal,
    viewNames: viewNames,
    activator: disabled ? newTab : /*#__PURE__*/React.createElement(Tooltip, {
      content: i18n.translate('Polaris.Tabs.newViewTooltip'),
      preferredPosition: "above",
      hoverDelay: 400
    }, newTab)
  })) : null))), panelMarkup);
};

export { AlphaTabs };
