import React from 'react';
import { bool, string, number, func, arrayOf, shape, oneOfType } from 'prop-types';
import { Field } from 'redux-form';
import {
  PROP_ASYNC_STATUS,
  SHAPE_ASYNC_STATUS_INITIAL,
  determineAsyncStatus,
  DEFAULT_RECT,
  debounce,
  throttle,
} from '@vincent_za/utils';
import { TextField } from '../../form';

import { CL_DROPDOWN_MENU } from '../constants';
import { DropdownWrap, MenuScroll as Menu } from '../shared';

class Base extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isOpen: false,
      isFocused: false,
      isMenuCutOff: false,
      clientRect: DEFAULT_RECT.getBoundingClientRect(),
    };
    this.timeoutID = undefined;
    this.onSearchDebounced = debounce(200)(this.onSearchDebounced);
    this.updateRectScroll = throttle(200)(this.updateRectScroll);
    this.updateRectResize = throttle(200)(this.updateRectResize);
  }

  componentDidMount() {
    const { domScrollSelector } = this.props;
    const scrollSelector = domScrollSelector ? document.querySelector(domScrollSelector) : window;
    scrollSelector.addEventListener('scroll', this.updateRectScroll);
    window.addEventListener('resize', this.updateRectResize);
    this.setState({
      clientRect: this.dropdownRef.getBoundingClientRect(),
    });
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextState.isOpen !== this.state.isOpen) return true;
    if (nextState.isFocused !== this.state.isFocused) return true;
    if (nextState.isMenuCutOff !== this.state.isMenuCutOff) return true;
    if (nextState.clientRect !== this.state.clientRect) return true;
    if (nextProps.isHidden !== this.props.isHidden) return true;
    if (nextProps.isClick !== this.props.isClick) return true;
    if (nextProps.isDisabled !== this.props.isDisabled) return true;
    if (nextProps.isInfo !== this.props.isInfo) return true;
    if (nextProps.activeItem !== this.props.activeItem) return true;
    if (nextProps.renderHeader !== this.props.renderHeader) return true;
    if (nextProps.renderMenu !== this.props.renderMenu) return true;
    if (nextProps.asyncStatus !== this.props.asyncStatus) return true;
    if (nextProps.asyncStatusList !== this.props.asyncStatusList) return true;
    return false;
  }

  componentDidUpdate() {
    const { isOpen, isMenuCutOff } = this.state;
    if (isOpen) {
      const { y: menuY, height: menuHeight } = this.menuRef.getBoundingClientRect();
      // const { height: appHeight } = document.querySelector('#app').getBoundingClientRect();
      // const isMenuCutOffUpdated = (menuY + menuHeight) > appHeight;
      const isMenuCutOffUpdated = false;
      if (!isMenuCutOff && isMenuCutOffUpdated === true) this.setState({ isMenuCutOff: true });
    }
  }

  componentWillUnmount() {
    const { domScrollSelector } = this.props;
    const scrollSelector = domScrollSelector ? document.querySelector(domScrollSelector) : window;
    clearTimeout(this.timeoutID);
    scrollSelector.removeEventListener('scroll', this.updateRectScroll);
    window.removeEventListener('resize', this.updateRectResize);
  }

  onSearchDebounced = (event) => {
    this.props.onSearch(event.target.value);
  }

  onBlur = () => {
    this.timeoutID = setTimeout(() => {
      if (this.state.isOpen) {
        this.setState({
          isOpen: false,
          isFocused: false,
        });
      }
    }, 0);
  }

  onFocus = () => {
    clearTimeout(this.timeoutID);
    if (!this.state.isOpen) {
      this.setState({
        isFocused: true,
      });
    }
  }

  onItemClick = (item) => {
    const { isCloseOnSelect, onItemClick } = this.props;
    this.onSearchDebounced.cancel();
    onItemClick(item);
    isCloseOnSelect && this.toggleDropdown();
  }

  toggleDropdown = () => {
    const { state: { isOpen }, props: { isDisabled, isInfo, onToggleDropdownCb, asyncStatus, asyncStatusList } } = this;
    const { isError } = determineAsyncStatus({ asyncStatus, asyncStatusList });
    clearTimeout(this.timeoutID);
    if (isDisabled || isInfo || isError) return this.setState({ isOpen: false, isFocused: false });
    this.updateRectResize();
    if (isOpen) {
      this.setState({ isOpen: false, isFocused: false });
    } else {
      this.setState({ isOpen: true, isFocused: true });
    }
    onToggleDropdownCb && onToggleDropdownCb();
  }

  addDropdownRef = (el) => { this.dropdownRef = el; };
  addMenuRef = (el) => { this.menuRef = el; };

  updateRectScroll = () => {
    !this.props.isInWidget && this.setState({
      clientRect: this.dropdownRef.getBoundingClientRect(),
    });
  }

  updateRectResize = () => {
    this.setState({
      clientRect: this.dropdownRef.getBoundingClientRect(),
    });
  }

  render() {
    const { isMenuCutOff, clientRect } = this.state;
    const {
      isFormVariant,
      isHeaderStretchWidth,
      renderHeader,
      renderMenu,
      isHidden,
      isMenuHeaderWidth,
      isMenuHeaderWidthMin,
      isClick,
      isDisabled,
      isInfo,
      isTop,
      isRight,
      isBottom,
      isNoMaxHeight,
      isInWidget,
      activeItem,
      fieldName,
      asyncStatus,
      asyncStatusList,
      className,
    } = this.props;
    const { isError } = determineAsyncStatus({ asyncStatus, asyncStatusList });
    const isOpen = this.state.isFocused && this.state.isOpen;
    const hasActiveItem = activeItem !== '' && activeItem !== undefined;

    return (
      <DropdownWrap
        isHeaderStretchWidth={isHeaderStretchWidth}
        isClick={isClick}
        isOpen={isOpen}
        isDisabled={isDisabled}
        isInfo={isInfo}
        isError={isError}
        isHidden={isHidden}
        className={className}
        tabIndex={0}
        onBlur={this.onBlur}
        onFocus={this.onFocus}
        onMouseEnter={!isClick ? this.toggleDropdown : () => {}}
        onMouseLeave={!isClick ? this.toggleDropdown : () => {}}
        ref={this.addDropdownRef}
      >
        {renderHeader({ isOpen, hasActiveItem, onClick: isClick ? this.toggleDropdown : () => {} })}
        {fieldName && <Field isHidden name={fieldName} id={fieldName} component={TextField} />}
        {
          isOpen &&
          <Menu
            isFormVariant={isFormVariant}
            isClick={isClick}
            isOpen={isOpen}
            isError={isError}
            isDisabled={isDisabled}
            isInfo={isInfo}
            isMenuHeaderWidth={isMenuHeaderWidth}
            isMenuHeaderWidthMin={isMenuHeaderWidthMin}
            isLightScrollVariant
            isNoMaxHeight={isNoMaxHeight}
            isTop={isTop}
            isRight={isRight}
            isBottom={isBottom}
            isMenuCutOff={isMenuCutOff}
            isInWidget={isInWidget}
            clientRect={clientRect}
            className={CL_DROPDOWN_MENU}
            passedRef={this.addMenuRef}
          >
            {renderMenu({
              isMenuCutOff,
              clientRect,
              onClick: this.onItemClick,
              onSearchDebounced: this.onSearchDebounced,
            })}
          </Menu>
        }
      </DropdownWrap>
    );
  }
}

Base.propTypes = {
  isCloseOnSelect: bool,
  isFormVariant: bool,
  isHeaderStretchWidth: bool,
  isHidden: bool,
  isMenuHeaderWidth: bool,
  isMenuHeaderWidthMin: bool,
  isClick: bool,
  isDisabled: bool,
  isInfo: bool,
  isTop: bool,
  isRight: bool,
  isBottom: bool,
  isNoMaxHeight: bool,
  isInWidget: bool.isRequired,
  activeItem: oneOfType([string, number]),
  domScrollSelector: string,
  fieldName: string,
  renderHeader: func.isRequired,
  renderMenu: func.isRequired,
  onToggleDropdownCb: func,
  onItemClick: func,
  onSearch: func,
  asyncStatus: shape(PROP_ASYNC_STATUS),
  asyncStatusList: arrayOf(shape(PROP_ASYNC_STATUS)),
  className: string,
};

Base.defaultProps = {
  isCloseOnSelect: true,
  isFormVariant: false,
  isHeaderStretchWidth: false,
  isHidden: false,
  isMenuHeaderWidth: false,
  isMenuHeaderWidthMin: false,
  isClick: false,
  isDisabled: false,
  isInfo: false,
  isTop: false,
  isRight: false,
  isBottom: false,
  isNoMaxHeight: false,
  activeItem: undefined,
  domScrollSelector: undefined,
  fieldName: undefined,
  onToggleDropdownCb() {},
  onItemClick() {},
  onSearch() {},
  asyncStatus: SHAPE_ASYNC_STATUS_INITIAL,
  asyncStatusList: [],
  className: undefined
};

export default Base;
