import React from 'react';

export interface IInfiniteScrollProps {
  loadMore: () => Promise<any>;
  hasNextPage: boolean;
  parentNode: HTMLElement;
  loader: React.ReactNode;
  isReverse: boolean;
  threshold: number;
}

export interface IInfiniteScrollState {
  isLoadingNext: boolean;
}

let offset = 0;

class InfiniteScroll extends React.Component<IInfiniteScrollProps, IInfiniteScrollState> {
  static defaultProps = {
    isReverse: false,
    threshold: 350,
  };

  constructor(props: IInfiniteScrollProps) {
    super(props);

    this.state = {
      isLoadingNext: false,
    };
  }

  componentDidMount() {
    this.props.parentNode.addEventListener('scroll', this.onScroll);
  }

  componentWillUnmount() {
    this.props.parentNode.removeEventListener('scroll', this.onScroll);
  }

  render() {
    const { children, hasNextPage, loader, isReverse } = this.props;

    return (
      <>
        {hasNextPage && isReverse && loader}
        {children}
        {hasNextPage && !isReverse && loader}
      </>
    );
  }

  private onScroll = async () => {
    const { parentNode, loadMore, threshold, hasNextPage, isReverse } = this.props;
    offset = parentNode.scrollTop - threshold;

    if (this.state.isLoadingNext || !hasNextPage) {
      return;
    }

    const oldScrollHeight = parentNode.scrollHeight;
    const shouldLoadMore = offset <= 0;

    if (!shouldLoadMore) {
      return;
    }

    this.setState({
      isLoadingNext: true,
    });

    await loadMore();

    if (isReverse) {
      parentNode.scrollTop =
        parentNode.scrollHeight - oldScrollHeight + threshold + offset;
    }

    this.setState({
      isLoadingNext: false,
    });
  };
}

export default InfiniteScroll;
