const SekshiModule = require('../Module')
const mongoose = require('mongoose')

const Achievement = mongoose.modelNames().indexOf('Achievement') === -1
  ? mongoose.model('Achievement', {
      _id: String,
      image: String,
      description: String
    })
  : mongoose.model('Achievement')

const AchievementUnlock = mongoose.modelNames().indexOf('AchievementUnlock') === -1
  ? mongoose.model('AchievementUnlock', {
      achievement: { type: String, ref: 'Achievement',  },
      user: { type: Number, ref: 'User' },
      giver: { type: Number, ref: 'User' },
      time: { type: Date, default: Date.now }
    })
  : mongoose.model('AchievementUnlock')

export default class Achievements extends SekshiModule {

  constructor(sekshi, options) {
    super(sekshi, options)

    this.version = '0.1.0'
    this.description = 'Achievements.'

    this.permissions = {
      addachievement: sekshi.USERROLE.COHOST,
      giveachievement: sekshi.USERROLE.BOUNCER,
      ga: sekshi.USERROLE.BOUNCER
    }

    this.ninjaVanish = [ 'addachievement' ]

    this.Achievement = Achievement
    this.AchievementUnlock = AchievementUnlock
  }

  defaultOptions() {
    return {
      showDescription: false
    }
  }

  getUnlockedAchievements(uid) {
    return AchievementUnlock.find({ user: uid }, [ 'achievement' ]).lean().exec()
      .then(unlocked => unlocked.map(ach => ach.achievement))
      .catch(e => [])
  }

  hasAchievement(id, name) {
    return AchievementUnlock.count({
      user: id,
      achievement: name
    }).exec().then(
      num => num > 0,
      e => false
    )
  }

  defineAchievement({ name, image, description }) {
    return Achievement.create({ _id: name, image, description })
  }

  unlockAchievement(user, achievement) {
    // normalise achievement name
    achievement = achievement.toLowerCase()
    return this.hasAchievement(user, achievement).then(has => {
      if (has) {
        return null
      }
      return AchievementUnlock.create({
        user,
        achievement,
      })
    })
  }

  addachievement({ username }, name, image, ...description) {
    description = description.join(' ')
    let e
    if (!name) e = `You have to enter a name.`
    if (!image) e = `You have to add an image link.`

    if (e) return this.sekshi.sendChat(`@${username} ${e}`)

    // normalise achievement names
    name = name.toLowerCase()

    this.defineAchievement({ name, image, description }).then(
      ach => { this.sekshi.sendChat(`@${username} Created achievement "${ach.id}"!`) },
      e => { console.error(e.stack || e.message) }
    )
  }

  giveachievement(user, targetName, achievement) {
    const target = this.sekshi.getUserByName(targetName)
    if (target) {
      this.unlockAchievement(target.id, achievement).then(unlock => {
        if (unlock == null) {
          this.sekshi.sendChat(`@${user.username} ${target.username} ` +
                               `already has that achievement!`)
          return
        }
        unlock.set('giver', user.id).save()
        return Achievement.findById(unlock.achievement).exec().then(ach => {
          if (ach.description && this.options.showDescription) {
            this.sekshi.sendChat(ach.description)
          }
          this.sekshi.sendChat(`@${target.username} unlocked achievement ${ach.image}`)
        })
      }).then(null, e => {
        console.error(e.stack || e.message)
      })
    }
  }
  ga(...args) {
    this.giveachievement(...args)
  }

}
@@ -35,16 +35,54 @@
 odule')%0A
+const command = require('../command')%0A
 const mo
@@ -883,213 +883,8 @@
 .'%0A%0A
-    this.permissions = %7B%0A      addachievement: sekshi.USERROLE.COHOST,%0A      giveachievement: sekshi.USERROLE.BOUNCER,%0A      ga: sekshi.USERROLE.BOUNCER%0A    %7D%0A%0A    this.ninjaVanish = %5B 'addachievement' %5D%0A%0A
     
@@ -1866,16 +1866,453 @@
 %7D)%0A  %7D%0A%0A
+  notifyUnlocked(unlock) %7B%0A    let target = this.sekshi.getUserByID(unlock.user)%0A    return Achievement.findById(unlock.achievement).then(ach =%3E %7B%0A      if (ach.description && this.options.showDescription) %7B%0A        this.sekshi.sendChat(ach.description)%0A      %7D%0A      this.sekshi.sendChat(%60@$%7Btarget.username%7D unlocked achievement $%7Bach.image%7D%60)%0A    %7D)%0A  %7D%0A%0A  @command('addachievement', %7B role: command.ROLE.COHOST, ninjaVanish: true %7D)%0A
   addach
@@ -2856,16 +2856,84 @@
  )%0A  %7D%0A%0A
+  @command('giveachievement', 'ga', %7B role: command.ROLE.BOUNCER %7D)%0A
   giveac
@@ -3377,294 +3377,34 @@
 urn 
-Achievement.findById(unlock.achievement).exec().then(ach =%3E %7B%0A          if (ach.description && this.options.showDescription) %7B%0A            this.sekshi.sendChat(ach.description)%0A          %7D%0A          this.sekshi.sendChat(%60@$%7Btarget.username%7D unlocked achievement $%7Bach.image%7D%60)%0A        %7D
+this.notifyUnlocked(unlock
 )%0A  
@@ -3494,61 +3494,8 @@
 %0A  %7D
-%0A  ga(...args) %7B%0A    this.giveachievement(...args)%0A  %7D
 %0A%0A%7D
+%0A

upgrade to `@command` & extract unlock notification to API method