'use strict'
const Trend = require('../models/trend')
const config = require('../../config')
const sentimentUtils = require('./sentiment-utils')

/**
 * Contains utility functions used to query and update the database.
 */
const dbUtils = {
  /**
   * Removes all trends from the database not located in the currTrends array.
   *
   * @param  {Array} currTrends Array of trend names (as strings) that are still trending, trends not in this array will be removed from the db
   */
  removeOldTrends: function (currTrends) {
    return new Promise((resolve, reject) => {
      Trend.remove({name: {$nin: currTrends}})
      .then(resolve)
      .catch(reject)
    })
  },

  /**
   * Given a specific trend, update its document in the database with the given
   * information, or create a new document for it if it does not already exist
   * in the database. Returns a promise that is resolved after the trend has
   * been saved.
   *
   * @param  {Object} trendData Data about the trend returned from the trends module
   * @param  {String} trendData.name Name of trend
   * @param  {String} trendData.rank Trend popularity rank
   * @param  {String} trendData.locations Array of country code locations in which the trend is trending
   *
   * @param  {Array} newsArticles Array of news articles for the trend returned from the news module
   *
   * @param  {Array} tweets Array of popular tweets for the trend returned from the tweet-search module
   *
   * @param  {Object} streamData Data returned from the tweetStream for this trend (can be undefined if the trend just started trending)
   * @param  {Number} streamData.sentiment Sentiment score collected for the trend by the tweetStream
   * @param  {String} streamData.sentiment_description Human readable description of the sentiment score
   * @param  {Number} streamData.tweets_analyzed Number of tweets analyzed to get the sentiment score
   * @param  {Array}  streamData.keywords Array of keywords collected from the tweetStream
   *
   * @return {Promise}
   */
  processTrend: function (trendData, newsArticles, tweets, streamData) {
    return new Promise((resolve, reject) => {
      const fullTrendData = {
        name: trendData.name,
        rank: trendData.rank,
        locations: trendData.locations,
        articles: newsArticles,
        tweets: tweets,
        sentiment_score: streamData ? streamData.sentiment : null,
        sentiment_description: streamData ? sentimentUtils.getSentimentDescription(streamData.sentiment) : null,
        tweets_analyzed: streamData ? streamData.tweets_analyzed : 0,
        keywords: streamData ? streamData.keywords : []
      }

      // Try to find the trend
      Trend.findOne({name: trendData.name})
      .then(doc => {
        // If trend exists
        if (doc) {
          dbUtils.updateExistingTrend(doc, fullTrendData).then(resolve)
        } else {
          dbUtils.createNewTrend(fullTrendData).then(resolve)
        }
      }).catch(reject)
    })
  },

  /**
   * Given the existing document for a trend in the db, and newly collected
   * data for a trend, update the trend's data in the db. Returns a promise
   *
   * @param  {Object} existingTrendData MongoDB document already existing in the database for the trend
   * @param  {Object} currentTrendData Object of the same form as existingTrendData, containing new information
   * @return {Promise} Promise resolved after trend document is updated
   */
  updateExistingTrend: function (existingTrendData, currentTrendData) {
    return new Promise((resolve, reject) => {
      const newTweetsAnalyzed = existingTrendData.tweets_analyzed + currentTrendData.tweets_analyzed

      // Calculate the new sentiment score (weighting for tweets_analyzed and avoiding dividing by zero)
      let newSentimentScore = newTweetsAnalyzed > 0
        ? (currentTrendData.sentiment_score * currentTrendData.tweets_analyzed +
        existingTrendData.sentiment_score * existingTrendData.tweets_analyzed) /
        newTweetsAnalyzed : null
      newSentimentScore = Math.round(newSentimentScore * 1000) / 1000 // Round to nearest thousandth

      // Create a new keyword array (removing duplicates)
      const keywordsExisting = {}
      let newKeywords = existingTrendData.keywords.concat(currentTrendData.keywords)
      .filter(keyword => {
        if (keywordsExisting[keyword.word]) {
          return false
        } else {
          keywordsExisting[keyword.word] = true
          return true
        }
      })
      newKeywords.sort((a, b) => {
        return b.occurences - a.occurences
      })
      newKeywords = newKeywords.slice(0, config.maxKeywordsPerTrend)

      Trend.findOneAndUpdate({name: existingTrendData.name},
        {
          $set: {
            sentiment_score: newSentimentScore,
            sentiment_description: newSentimentScore !== null ? sentimentUtils.getSentimentDescription(newSentimentScore) : null,
            tweets_analyzed: newTweetsAnalyzed,
            rank: currentTrendData.rank,
            keywords: newKeywords,
            tweets: currentTrendData.tweets,
            articles: currentTrendData.articles
          }
        })
      .then(resolve)
      .catch(reject)
    })
  },

  /**
   * Create a new trend in the database. Returns a promise
   *
   * @param  {Object} trendData Data for a trend as specified in the trend model
   * @return {Promise} Promise resolved arter trend is created
   */
  createNewTrend: function (trendData) {
    return new Promise((resolve, reject) => {
      new Trend(trendData).save()
      .then(resolve)
      .catch(reject)
    })
  }
}

module.exports = dbUtils

@@ -4008,16 +4008,17 @@
  : null%0A
+%0A
       ne
@@ -4034,16 +4034,52 @@
 tScore =
+ newSentimentScore === null ? null :
  Math.ro
@@ -4145,16 +4145,28 @@
 ousandth
+ if not null
 %0A%0A      

Fix sentiment score not being null when no tweets bug