import React, { PureComponent, Children, isValidElement } from 'react';
import PropTypes from 'prop-types';
import _isEqual from 'lodash/isEqual';
import { Sticky } from 'react-sticky';
import omit from 'lodash/omit';
import Section from '../../Section';

import './tabs-container.scss'; // eslint-disable-line no-unused-vars

const { Column } = Section;

const propTypes = {
  children: PropTypes.node,
  scrollSpy: PropTypes.object,
  sticky: PropTypes.object,
  withHashTag: PropTypes.bool,
  tabDirection: PropTypes.oneOf(['column', 'row'])
};

const defaultProps = {
  children: undefined,
  scrollSpy: null,
  sticky: null,
  withHashTag: false,
  tabDirection: 'column'
};

class TabsContainer extends PureComponent {

  state = {
    selectedIndex: 0,
    targetItems: [],
    inViewState: []
  };

  targetChildren = {}; // eslint-disable-line react/sort-comp
  timer = null; // eslint-disable-line react/sort-comp

  componentDidMount() {
    const { scrollSpy } = this.props;
    if (scrollSpy) {
      this._initFromProps(this.props);
      window.addEventListener('scroll', this._handleSpy);
    }
  }

  componentWillReceiveProps(nextProps) {
    this._initFromProps(nextProps);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (!_isEqual(this.props, nextProps) || !_isEqual(this.state, nextState));
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this._handleSpy);
  }

  onTabsChange = selectedIndex => {
    this.setState({
      selectedIndex
    });
  };

  _getElemsViewState = () => {
    const inView = [];
    const outView = [];
    const viewStatusList = [];
    const { targetItems } = this.state;
    let hasInViewAlready = false;

    for (let i = 0, max = targetItems.length; i < max; i += 1) {
      if (!this.targetChildren[targetItems[i]]) {
        this.targetChildren[targetItems[i]] = document.getElementById(targetItems[i]);
      }
      const currentContent = this.targetChildren[targetItems[i]];
      const isInView = hasInViewAlready ? false : this._isInView(currentContent, i);
      if (isInView) {
        hasInViewAlready = true;
        inView.push(currentContent);
      } else {
        outView.push(currentContent);
      }
      viewStatusList.push(isInView);
    }

    return { inView, outView, viewStatusList };
  };

  _isInView = el => {
    if (!el) return false;
    const { topOffset = 0 } = this.props.scrollSpy || {};
    const { top, height } = el.getBoundingClientRect();
    return ((top + height + topOffset) > 0 || top > 0);
  };

  _handleSpy = () => {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(() => this.setState({ inViewState: this._getElemsViewState().viewStatusList }), 20);
  };

  _initFromProps = props => {
    const { children } = props || this.props;
    const targetItems = Children
      .toArray(children)
      .reduce((resultArray, child) => {
        if (isValidElement(child)) {
          const { props: { to = '', href = '' }, type: { componentName } } = child;
          if (componentName === 'Tab') {
            const target = to.replace('#', '') || href.replace('#', '');
            if (resultArray.indexOf(target) === -1) resultArray.push(target);
          }
        }
        return resultArray;
      }, []) || [];
    this.targetChildren = {};
    this.setState({
      targetItems
    });
    this._handleSpy();
  };

  renderNav({ style } = {}) {
    const { children, scrollSpy, withHashTag, tabDirection, ...props } = this.props;
    const { topOffset = 0 } = scrollSpy || {};
    const { selectedIndex, inViewState } = this.state;
    delete props.topOffset;
    let i = 0;
    return (
      <nav
        style={{ ...style, zIndex: 10 }}
        styleName={`${tabDirection}`}
      >
        {Children.map(children, child => {
          if (!isValidElement(child)) {
            return child;
          }
          const { type: { componentName } } = child;
          let myChild;
          if (componentName === 'Tab') {
            if (scrollSpy) {
              myChild = React.cloneElement(child, {
                active: inViewState[i],
                offset: topOffset,
                index: i,
                scrollSpy,
                withHashTag,
                duration: 300,
                tabDirection
              });
            } else {
              myChild = React.cloneElement(child, {
                onChange: this.onTabsChange,
                active: i === selectedIndex,
                index: i,
                tabDirection
              });
            }
            i += 1;
            return myChild;
          }
          return child;
        })}
      </nav>
    );
  }

  render() {
    const { sticky, tabDirection, ...props } = this.props;
    let TabsContainerComponent;
    switch (tabDirection) {
      case 'column':
        TabsContainerComponent = Column;
        break;
      case 'row':
      default:
        TabsContainerComponent = 'div';
    }

    return (
      <TabsContainerComponent
        {...omit(props, Object.keys(propTypes))}
      >
        {sticky ?
          <Sticky {...sticky}>
            {({ distanceFromBottom, style: { top = 0, ...style } }) => {
              const topOffset = -1 * sticky.topOffset;
              const styleTopOffset = Math.max(
                0, topOffset > distanceFromBottom ? topOffset - (topOffset - distanceFromBottom) : topOffset
              );
              style.top = top + styleTopOffset; // eslint-disable-line no-param-reassign
              return this.renderNav({ style });
            }}
          </Sticky>
          : this.renderNav()}
      </TabsContainerComponent>
    );
  }
}

TabsContainer.propTypes = propTypes;
TabsContainer.defaultProps = defaultProps;
TabsContainer.componentName = 'TabsContainer';

export default TabsContainer;
