import app from '../../forum/app';
import Component from '../../common/Component';
import listItems from '../../common/helpers/listItems';
import Button from '../../common/components/Button';
import Link from '../../common/components/Link';
import LoadingIndicator from '../../common/components/LoadingIndicator';
import Discussion from '../../common/models/Discussion';
import ItemList from '../../common/utils/ItemList';
import Tooltip from '../../common/components/Tooltip';

/**
 * The `NotificationList` component displays a list of the logged-in user's
 * notifications, grouped by discussion.
 */
export default class NotificationList extends Component {
  view() {
    const state = this.attrs.state;

    return (
      <div className="NotificationList">
        <div className="NotificationList-header">
          <h4 className="App-titleControl App-titleControl--text">{app.translator.trans('core.forum.notifications.title')}</h4>

          <div className="App-primaryControl">{this.controlItems().toArray()}</div>
        </div>

        <div className="NotificationList-content">{this.content(state)}</div>
      </div>
    );
  }

  controlItems() {
    const items = new ItemList();
    const state = this.attrs.state;

    items.add(
      'mark_all_as_read',
      <Tooltip text={app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip')}>
        <Button
          className="Button Button--link"
          data-container=".NotificationList"
          icon="fas fa-check"
          title={app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip')}
          onclick={state.markAllAsRead.bind(state)}
        />
      </Tooltip>,
      70
    );

    items.add(
      'delete_all',
      <Tooltip text={app.translator.trans('core.forum.notifications.delete_all_tooltip')}>
        <Button
          className="Button Button--link"
          data-container=".NotificationList"
          icon="fas fa-trash-alt"
          title={app.translator.trans('core.forum.notifications.delete_all_tooltip')}
          onclick={() => {
            if (confirm(app.translator.trans('core.forum.notifications.delete_all_confirm'))) {
              state.deleteAll.call(state);
            }
          }}
        />
      </Tooltip>,
      50
    );

    return items;
  }

  content(state) {
    if (state.isLoading()) {
      return <LoadingIndicator className="LoadingIndicator--block" />;
    }

    if (state.hasItems()) {
      return state.getPages().map((page) => {
        const groups = [];
        const discussions = {};

        page.items.forEach((notification) => {
          const subject = notification.subject();

          if (typeof subject === 'undefined') return;

          // Get the discussion that this notification is related to. If it's not
          // directly related to a discussion, it may be related to a post or
          // other entity which is related to a discussion.
          let discussion = null;
          if (subject instanceof Discussion) discussion = subject;
          else if (subject && subject.discussion) discussion = subject.discussion();

          // If the notification is not related to a discussion directly or
          // indirectly, then we will assign it to a neutral group.
          const key = discussion ? discussion.id() : 0;
          discussions[key] = discussions[key] || { discussion: discussion, notifications: [] };
          discussions[key].notifications.push(notification);

          if (groups.indexOf(discussions[key]) === -1) {
            groups.push(discussions[key]);
          }
        });

        return groups.map((group) => {
          const badges = group.discussion && group.discussion.badges().toArray();

          return (
            <div className="NotificationGroup">
              {group.discussion ? (
                <Link className="NotificationGroup-header" href={app.route.discussion(group.discussion)}>
                  {badges && !!badges.length && <ul className="NotificationGroup-badges badges">{listItems(badges)}</ul>}
                  <span>{group.discussion.title()}</span>
                </Link>
              ) : (
                <div className="NotificationGroup-header">{app.forum.attribute('title')}</div>
              )}

              <ul className="NotificationGroup-content">
                {group.notifications.map((notification) => {
                  const NotificationComponent = app.notificationComponents[notification.contentType()];
                  return (
                    !!NotificationComponent && (
                      <li>
                        <NotificationComponent notification={notification} />
                      </li>
                    )
                  );
                })}
              </ul>
            </div>
          );
        });
      });
    }

    return <div className="NotificationList-empty">{app.translator.trans('core.forum.notifications.empty_text')}</div>;
  }

  oncreate(vnode) {
    super.oncreate(vnode);

    this.$notifications = this.$('.NotificationList-content');

    // If we are on the notifications page, the window will be scrolling and not the $notifications element.
    this.$scrollParent = this.inPanel() ? this.$notifications : $(window);

    this.boundScrollHandler = this.scrollHandler.bind(this);
    this.$scrollParent.on('scroll', this.boundScrollHandler);
  }

  onremove(vnode) {
    super.onremove(vnode);

    this.$scrollParent.off('scroll', this.boundScrollHandler);
  }

  scrollHandler() {
    const state = this.attrs.state;

    // Whole-page scroll events are listened to on `window`, but we need to get the actual
    // scrollHeight, scrollTop, and clientHeight from the document element.
    const scrollParent = this.inPanel() ? this.$scrollParent[0] : document.documentElement;

    // On very short screens, the scrollHeight + scrollTop might not reach the clientHeight
    // by a fraction of a pixel, so we compensate for that.
    const atBottom = Math.abs(scrollParent.scrollHeight - scrollParent.scrollTop - scrollParent.clientHeight) <= 1;

    if (state.hasNext() && !state.isLoadingNext() && atBottom) {
      state.loadNext();
    }
  }

  /**
   * If the NotificationList component isn't in a panel (e.g. on NotificationPage when mobile),
   * we need to listen to scroll events on the window, and get scroll state from the body.
   */
  inPanel() {
    return this.$notifications.css('overflow') === 'auto';
  }
}
