import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import './accordion.scss';

class AccordionItem extends PureComponent {
  constructor(props) {
    super(props);

    // Defaults the dropdown to be closed
    if (props.open) {
      this.state = {
        isClosed: false,
        shouldSwitchAutoOnNextCycle: false,
        height: 'auto',
        transition: 'none',
        hasBeenOpened: true,
        overflow: props.overflowWhenOpen,
        inTransition: false
      };
    } else {
      this.state = {
        isClosed: true,
        shouldSwitchAutoOnNextCycle: false,
        height: 0,
        transition: `height ${props.transitionTime}ms ${props.easing}`,
        hasBeenOpened: false,
        overflow: 'hidden',
        inTransition: false
      };
    }
  }

  componentDidUpdate = (prevProps, prevState) => {
    if (this.state.shouldOpenOnNextCycle) {
      this.continueOpenCollapsible();
    }

    if (prevState.height === 'auto' && this.state.shouldSwitchAutoOnNextCycle === true) {
      window.setTimeout(() => { // Set small timeout to ensure a true re-render
        this.setState({
          height: 0,
          overflow: 'hidden',
          isClosed: true,
          shouldSwitchAutoOnNextCycle: false
        });
      }, 50);
    }

    // If there has been a change in the open prop (controlled by accordion)
    if (prevProps.open !== this.props.open) {
      if (this.props.open === true) {
        this.openCollapsible();
      } else {
        this.closeCollapsible();
      }
    }
  }

  closeCollapsible = () => {
    this.setState({
      shouldSwitchAutoOnNextCycle: true,
      height: this.inner.offsetHeight,
      transition: `height ${this.props.transitionTime}ms ${this.props.easing}`,
      inTransition: true
    });
  }

  openCollapsible = () => {
    this.setState({
      inTransition: true,
      shouldOpenOnNextCycle: true
    });
  }

  continueOpenCollapsible = () => {
    this.setState({
      height: this.inner.offsetHeight,
      transition: `height ${this.props.transitionTime}ms ${this.props.easing}`,
      isClosed: false,
      hasBeenOpened: true,
      inTransition: true,
      shouldOpenOnNextCycle: false
    });
  }

  handleTriggerClick = event => {
    event.preventDefault();

    if (this.props.triggerDisabled) {
      return;
    }

    if (this.props.handleTriggerClick) {
      this.props.handleTriggerClick(this.props.accordionPosition);
    } else {
      if (this.state.isClosed === true) {
        this.openCollapsible();
        this.props.onOpening();
      } else {
        this.closeCollapsible();
        this.props.onClosing();
      }
    }
  }

  handleTransitionEnd = () => {
    // Switch to height auto to make the container responsive
    if (!this.state.isClosed) {
      this.setState({ height: 'auto', overflow: this.props.overflowWhenOpen, inTransition: false });
      this.props.onOpen();
    } else {
      this.setState({ inTransition: false });
      this.props.onClose();
    }
  }

  render() {
    const dropdownStyle = {
      height: this.state.height,
      WebkitTransition: this.state.transition,
      msTransition: this.state.transition,
      transition: this.state.transition,
      overflow: this.state.overflow
    };

    // If user wants different text when tray is open
    const trigger = (this.state.isClosed === false) && (this.props.triggerWhenOpen !== undefined)
                  ? this.props.triggerWhenOpen
                  : this.props.trigger;

    // Don't render children until the first opening of the Collapsible if lazy rendering is enabled
    const children = this.props.lazyRender
      && !this.state.hasBeenOpened
      && this.state.isClosed
      && !this.state.inTransition ? null : this.props.children;

    const triggerStyleName = classNames('trigger', {
      closed: this.state.isClosed,
      open: !this.state.isClosed,
      disabled: this.props.triggerDisabled
    });

    return (
      <div className={this.props.className} styleName={classNames('item', { 'item-open': !this.state.isClosed })}>
        <a
          styleName={triggerStyleName}
          onClick={this.handleTriggerClick}
          href="#"
        >
          {trigger}
        </a>
        <div
          styleName="outer"
          ref={ref => this.outer = ref}
          style={dropdownStyle}
          onTransitionEnd={this.handleTransitionEnd}
        >
          <div
            styleName="inner"
            ref={ref => this.inner = ref}
          >
            {children}
          </div>
        </div>
      </div>
    );
  }
}

AccordionItem.propTypes = {
  /** @ignore */
  className: PropTypes.string,
  transitionTime: PropTypes.number,
  easing: PropTypes.string,
  open: PropTypes.bool,
  accordionPosition: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  handleTriggerClick: PropTypes.func,
  onOpen: PropTypes.func,
  onClose: PropTypes.func,
  onOpening: PropTypes.func,
  onClosing: PropTypes.func,
  trigger: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.element
  ]),
  triggerWhenOpen:PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.element
  ]),
  triggerDisabled: PropTypes.bool,
  lazyRender: PropTypes.bool,
  overflowWhenOpen: PropTypes.oneOf([
    'hidden',
    'visible',
    'auto',
    'scroll',
    'inherit',
    'initial',
    'unset'
  ])
};

AccordionItem.defaultProps = {
  className: undefined,
  transitionTime: 200,
  easing: 'linear',
  open: false,
  triggerDisabled: false,
  lazyRender: false,
  overflowWhenOpen: 'hidden',
  onOpen: () => {},
  onClose: () => {},
  onOpening: () => {},
  onClosing: () => {}
};

export default AccordionItem;
