// @flow
import * as REACTION_TYPES from 'constants/reactions';
import * as ICONS from 'constants/icons';
import { SORT_COMMENTS_NEW, SORT_COMMENTS_BEST, SORT_COMMENTS_CONTROVERSIAL } from 'constants/comment';
import React, { useEffect } from 'react';
import classnames from 'classnames';
import CommentView from 'component/comment';
import Spinner from 'component/spinner';
import Button from 'component/button';
import Card from 'component/common/card';
import CommentCreate from 'component/commentCreate';
import usePersistedState from 'effects/use-persisted-state';
import { ENABLE_COMMENT_REACTIONS } from 'config';
import { sortComments } from 'util/comments';

type Props = {
  comments: Array<Comment>,
  fetchComments: string => void,
  fetchReacts: string => Promise<any>,
  uri: string,
  claimIsMine: boolean,
  myChannels: ?Array<ChannelClaim>,
  isFetchingComments: boolean,
  linkedComment: any,
  totalComments: number,
  fetchingChannels: boolean,
  reactionsById: { [string]: { [REACTION_TYPES.LIKE | REACTION_TYPES.DISLIKE]: number } },
};

function CommentList(props: Props) {
  const {
    fetchComments,
    fetchReacts,
    uri,
    comments,
    claimIsMine,
    myChannels,
    isFetchingComments,
    linkedComment,
    totalComments,
    fetchingChannels,
    reactionsById,
  } = props;
  const commentRef = React.useRef();
  const spinnerRef = React.useRef();
  const [sort, setSort] = usePersistedState('comment-sort', SORT_COMMENTS_BEST);
  const [activeChannel] = usePersistedState('comment-channel', '');
  const [start] = React.useState(0);
  const [end, setEnd] = React.useState(9);
  // Display comments immediately if not fetching reactions
  // If not, wait to show comments until reactions are fetched
  const [readyToDisplayComments, setReadyToDisplayComments] = React.useState(!ENABLE_COMMENT_REACTIONS);
  const linkedCommentId = linkedComment && linkedComment.comment_id;
  const hasNoComments = totalComments === 0;

  const moreBelow = totalComments - end > 0;
  // todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
  const isMyComment = (channelId: string) => {
    if (myChannels != null && channelId != null) {
      for (let i = 0; i < myChannels.length; i++) {
        if (myChannels[i].claim_id === channelId) {
          return true;
        }
      }
    }
    return false;
  };

  const handleMoreBelow = React.useCallback(() => {
    if (moreBelow) {
      setEnd(end + 10);
    }
  }, [end, setEnd, moreBelow]);

  useEffect(() => {
    fetchComments(uri);
  }, [fetchComments, uri]);

  useEffect(() => {
    if (totalComments && ENABLE_COMMENT_REACTIONS && !fetchingChannels) {
      fetchReacts(uri)
        .then(() => {
          setReadyToDisplayComments(true);
        })
        .catch(() => setReadyToDisplayComments(true));
    }
  }, [fetchReacts, uri, totalComments, activeChannel, fetchingChannels]);

  useEffect(() => {
    if (linkedCommentId && commentRef && commentRef.current) {
      commentRef.current.scrollIntoView({ block: 'start' });
      window.scrollBy(0, -100);
    }
  }, [linkedCommentId]);

  useEffect(() => {
    function handleCommentScroll(e) {
      // $FlowFixMe
      const rect = spinnerRef.current.getBoundingClientRect();

      const isInViewport =
        rect.top >= 0 &&
        rect.left >= 0 &&
        // $FlowFixMe
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        // $FlowFixMe
        rect.right <= (window.innerWidth || document.documentElement.clientWidth);

      if (isInViewport) {
        handleMoreBelow();
      }
    }

    if (!isFetchingComments && readyToDisplayComments && moreBelow && spinnerRef && spinnerRef.current) {
      window.addEventListener('scroll', handleCommentScroll);
    }

    return () => window.removeEventListener('scroll', handleCommentScroll);
  }, [moreBelow, handleMoreBelow, spinnerRef, isFetchingComments, readyToDisplayComments]);

  function prepareComments(arrayOfComments, linkedComment, isFetchingComments) {
    let orderedComments = [];

    if (linkedComment) {
      if (!linkedComment.parent_id) {
        orderedComments = arrayOfComments.filter(c => c.comment_id !== linkedComment.comment_id);
        orderedComments.unshift(linkedComment);
      } else {
        const parentComment = arrayOfComments.find(c => c.comment_id === linkedComment.parent_id);
        orderedComments = arrayOfComments.filter(c => c.comment_id !== linkedComment.parent_id);

        if (parentComment) {
          orderedComments.unshift(parentComment);
        }
      }
    } else {
      orderedComments = arrayOfComments;
    }
    return orderedComments;
  }

  // Default to newest first for apps that don't have comment reactions
  const sortedComments = ENABLE_COMMENT_REACTIONS ? sortComments(comments, reactionsById, sort) : comments;
  const displayedComments = readyToDisplayComments
    ? prepareComments(sortedComments, linkedComment).slice(start, end)
    : [];

  return (
    <Card
      title={
        totalComments > 0
          ? totalComments === 1
            ? __('1 comment')
            : __('%total_comments% comments', { total_comments: totalComments })
          : __('Leave a comment')
      }
      titleActions={
        <>
          {totalComments > 1 && ENABLE_COMMENT_REACTIONS && (
            <span className="comment__sort">
              <Button
                button="alt"
                label={'Best'}
                icon={ICONS.FIRE}
                onClick={() => setSort(SORT_COMMENTS_BEST)}
                className={classnames(`button-toggle`, {
                  'button-toggle--active': sort === SORT_COMMENTS_BEST,
                })}
              />
              <Button
                button="alt"
                label={'Controversial'}
                icon={ICONS.CONTROVERSIAL}
                onClick={() => setSort(SORT_COMMENTS_CONTROVERSIAL)}
                className={classnames(`button-toggle`, {
                  'button-toggle--active': sort === SORT_COMMENTS_CONTROVERSIAL,
                })}
              />
              <Button
                button="alt"
                label={'New'}
                icon={ICONS.NEW}
                onClick={() => setSort(SORT_COMMENTS_NEW)}
                className={classnames(`button-toggle`, {
                  'button-toggle--active': sort === SORT_COMMENTS_NEW,
                })}
              />
            </span>
          )}
          <Button button="alt" icon={ICONS.REFRESH} title={__('Refresh')} onClick={() => fetchComments(uri)} />
        </>
      }
      actions={
        <>
          <CommentCreate uri={uri} />
          {!isFetchingComments && hasNoComments && <div className="main--empty">{__('Be the first to comment!')}</div>}
          <ul className="comments" ref={commentRef}>
            {!isFetchingComments &&
              comments &&
              displayedComments &&
              displayedComments.map(comment => {
                return (
                  <CommentView
                    isTopLevel
                    key={comment.comment_id}
                    uri={uri}
                    authorUri={comment.channel_url}
                    author={comment.channel_name}
                    claimId={comment.claim_id}
                    commentId={comment.comment_id}
                    topLevelId={comment.comment_id}
                    message={comment.comment}
                    timePosted={comment.timestamp * 1000}
                    claimIsMine={claimIsMine}
                    commentIsMine={comment.channel_id && isMyComment(comment.channel_id)}
                    linkedComment={linkedComment}
                  />
                );
              })}
          </ul>
          {(isFetchingComments || moreBelow) && (
            <div className="main--empty" ref={spinnerRef}>
              <Spinner type="small" />
            </div>
          )}
        </>
      }
    />
  );
}

export default CommentList;
