import React from 'react';
import ReactDOM from 'react-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import FeedStory from './FeedStory';
import connectService from '../../../services/connect';
import {feedService, channelService} from '../../../services';
import isEqual from 'lodash/isEqual';
import forEach from 'lodash/forEach';
import some from 'lodash/some';
import moment from 'moment';

export class Feed extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      recievedLive: false,
      numNewStories: 0,
      orderedStories: this.orderStories(props.stories),
      keepScrollOnNewStories: false
    };
    this.scrollHeight = 0;
    this.scrollTop = 0;
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.stories !== this.props.stories) {
      const keepScroll = this.refs.feed.scrollTop > 5 && !this.props.isLoading;
      const newStories = nextProps.stories.toSet().subtract(this.props.stories.toSet());
      const newLive = newStories.filter(story => story.Live);

      this.setState({
        keepScrollOnNewStories: keepScroll,
        numNewStories: this.refs.feed.scrollTop > 5 ? this.state.numNewStories + newLive.size : 0,
        recievedLive: !newLive.isEmpty(),
        orderedStories: this.orderStories(nextProps.stories)
      });
    }
    if (nextProps.filters !== this.props.filters) {
      // Filters have changed, reset the scroll position
      // *Not* putting this in state: don't want it to trigger a render()
      this.resetScroll = true;
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (
      nextProps.stories !== this.props.stories ||
      nextProps.headerSpacing !== this.props.headerSpacing ||
      nextProps.textSize !== this.props.textSize ||
      !isEqual(nextProps.themes, this.props.themes) ||
      nextState.numNewStories !== this.state.numNewStories ||
      nextProps.isLoading !== this.props.isLoading
    );
  }

  componentWillUpdate(nextProps, nextState) {
    this.scrollHeight = this.refs.feed.scrollHeight;
    this.scrollTop = this.refs.feed.scrollTop;
    this.feedHeight = this.refs.feed.feedHeight;
  }

  componentDidUpdate(prevProps, prevState) {
    // Reset scroll after component update to prevent data flash
    if (this.resetScroll) {
      this.refs.feed.scrollTop = 0;
      // Don't reset on next render()
      this.resetScroll = false;
    } else if (this.state.keepScrollOnNewStories) {
      this.refs.feed.scrollTop = this.scrollTop + (this.refs.feed.scrollHeight - this.scrollHeight);
    }
    if (this.shouldLoadMore()) {
      this.addStories();
    }
  }

  getStoryTheme(story) {
    let theme = null;
    forEach(this.props.themes, (t) => {
      if (t.category === 'channel' && some(story.Channels, (c) => t.target.tids.indexOf(c.tid.toString()) !== -1)) {
        theme = t;
      } else if (t.category === 'source' && t.target.id === story.Type) {
        theme = t;
      } else if (t.category === 'source' && t.target.id === 'pr_story' && channelService.isPressReleaseType(story.Type)) {
        // Custom for PR's
        theme = t;
      }
    });
    return theme;
  }

  handleScroll(e) {
    if (e.target.scrollTop === 0) { this.setState({numNewStories: 0}); }
    const scrollPosition = e.target.scrollTop;
    const nearBottom = e.target.scrollHeight - e.target.clientHeight - 420;
    const scrolledNearBottom = scrollPosition >= nearBottom;
    const moreSpaceForStories = e.target.clientHeight - e.target.scrollHeight > 0;
    if (this.shouldLoadMore(scrolledNearBottom)) {this.addStories(); }
  }

  moreSpaceForStories() {
    const feedHeight = this.refs.feedContainer.clientHeight;
    if (feedHeight === 0) { return false; }
    const allStoriesHeight = this.refs.allStories.clientHeight;
    const remainingHeight = feedHeight - allStoriesHeight;
    return remainingHeight > -50;
  }

  addStories() {
    const earliestDate = this.state.orderedStories.last().CreatedAt;
    this.props.service.requestMoreStories({earliestDate: earliestDate}, this.props.entity);
  }

  shouldLoadMore(scrolledNearBottom) {
    const canLoadMore = !this.props.isLoading && this.props.moreStoriesExist;
    const loadConditionsMet = this.props.stories.size < 50 || this.moreSpaceForStories() || scrolledNearBottom;
    return canLoadMore && loadConditionsMet;
  }

  orderStories(stories) {
    return stories.sortBy(s => s.CreatedAt).reverse();
  }

  handleBodyClose(article) {
    const feedOffset = this.refs.feed.getBoundingClientRect().top;
    const articleOffset = article.getBoundingClientRect().top;
    const posRelToFeed = articleOffset - feedOffset;
    if (posRelToFeed < 0) {
      this.refs.feed.scrollTop += posRelToFeed;
    }
  }

  scrollToTop() {
    this.refs.feed.scrollTop = 0;
    this.scrollTop = 0;
    this.setState({numNewStories: 0});
  }

  renderNotification() {
    if (this.state.numNewStories > 0) {
      return (
        <div onClick={() => this.scrollToTop()} className="feed-notification">
          {this.state.numNewStories} new stor{this.state.numNewStories === 1 ? 'y' : 'ies'}
        </div>
      );
    }
  }

  renderLoader() {
    if (this.props.isLoading) {
      return (<div className="feed-loading"><i className="Loader Loader--dark"></i></div>);
    }
  }

  render() {
    const storyList = [];
    let startDay = moment();
    this.state.orderedStories.forEach((story, i) => {
      const currentDay = moment(+moment.utc(story.CreatedAt));
      if (!startDay.isSame(currentDay, 'day')) {
        storyList.push(
          <div key={currentDay} className="feed-date">
            {currentDay.format('dddd MMMM D, YYYY')}
          </div>
        );
        startDay = currentDay;
      }
      storyList.push(
        <FeedStory
          story={story}
          key={story.ID}
          headerSpacing={this.props.headerSpacing}
          theme={this.getStoryTheme(story)}
          onBodyClose={(event, article) => this.handleBodyClose(article)}
          textSize={this.props.textSize}
        />
      );
    });

    return (
      <div className="feed-container" ref="feedContainer" onScroll={(e) => this.handleScroll(e)} style={{fontSize: this.props.textSize + 'px'}}>
        {this.renderNotification()}
        <div className="feed" ref="feed">
          <div ref="allStories">
            <ReactCSSTransitionGroup
              transitionName="flash-yellow"
              transitionEnter={this.state.recievedLive}
              transitionEnterTimeout={10000}
              transitionLeave={false}
            >
              {storyList}
            </ReactCSSTransitionGroup>
          </div>
          {this.renderLoader()}
        </div>
      </div>
    );
  }
}

Feed.propTypes = {
  entity: React.PropTypes.number,
  stories: React.PropTypes.object,
  filters: React.PropTypes.object,
  themes: React.PropTypes.array,
  service: React.PropTypes.object,
  headerSpacing: React.PropTypes.number,
  textSize: React.PropTypes.number,
  moreStoriesExist: React.PropTypes.bool,
  isLoading: React.PropTypes.bool
};

Feed.defaultProps = {
  textSize: 12,
  headerspacing: 1.7
};

export default connectService(feedService)(Feed);
