mongoose = require 'mongoose'
EarnedAchievement = require '../models/EarnedAchievement'
LocalMongo = require '../../app/lib/LocalMongo'
util = require '../../app/core/utils'
log = require 'winston'

# Warning: To ensure proper functioning one must always `find` documents before saving them.
# Otherwise the schema's `post init` won't be triggered and the plugin can't keep track of changes
# TODO if this is still a common scenario I could implement a database hit after all, but only
# on the condition that it's necessary and still not too frequent in occurrence
AchievablePlugin = (schema, options) ->
  User = require '../models/User'  # Avoid mutual inclusion cycles
  Achievement = require '../models/Achievement'

  # Keep track the document before it's saved
  schema.post 'init', (doc) ->
    unless doc.unchangedCopy
      doc.unchangedCopy = doc.toObject()

  # Check if an achievement has been earned
  schema.post 'save', (doc) ->
    schema.statics.createNewEarnedAchievements doc

  schema.statics.createNewEarnedAchievements = (doc, unchangedCopy) ->
    unchangedCopy ?= doc.unchangedCopy
    isNew = not doc.isInit('_id') or not unchangedCopy

    if doc.isInit('_id') and not unchangedCopy
      log.warn 'document was already initialized but did not go through `init` and is therefore treated as new while it might not be'

    category = doc.constructor.collection.name
    loadedAchievements = Achievement.getLoadedAchievements()

    if category of loadedAchievements
      #log.debug 'about to save ' + category + ', number of achievements is ' + loadedAchievements[category].length
      docObj = doc.toObject()
      for achievement in loadedAchievements[category]
        do (achievement) ->
          query = achievement.get('query')
          return log.error("Empty achievement query for #{achievement.get('name')}.") if _.isEmpty query
          isRepeatable = achievement.get('proportionalTo')?
          alreadyAchieved = if isNew then false else LocalMongo.matchesQuery unchangedCopy, query
          newlyAchieved = LocalMongo.matchesQuery(docObj, query)
          return unless newlyAchieved and (not alreadyAchieved or isRepeatable)
          EarnedAchievement.createForAchievement(achievement, doc, {originalDocObj: unchangedCopy})

module.exports = AchievablePlugin
