
helper = require('./helper')

mongoose = require('mongoose-q')()
Q = require('q')
ElasticMongooseQ = require('../index')


#
# Elasticsearch setup
#
elasticClient = helper.elasticsearch.client
elasticIndex = helper.elasticsearch.index
elasticMongooseQ = new ElasticMongooseQ
  index: elasticIndex
  client: elasticClient

#
# Fish Class
#
FishSchema = helper.mongo.AnimalSchema
animalMapping = helper.elasticsearch.animalMapping

# Add the plugin giving the type and
FishSchema.plugin elasticMongooseQ.plugin(),
  type: 'fish'
  mapping: animalMapping
  toObjectMethod: 'toSearchObject'

# Mock method to test custom toObject with a delayed promise
FishSchema.methods.toSearchObject = (callback) ->
  deferred = Q.defer()
  setTimeout () =>
    deferred.resolve @toObject()
  , 50
  deferred.promise.nodeify(callback)

Fish = mongoose.model "Fish", FishSchema


#
# Mamal Class
#
MamalSchema = helper.mongo.AnimalSchema

# Add the plugin giving the type and
MamalSchema.plugin elasticMongooseQ.plugin(),
  type: 'mamal'
  mapping: animalMapping

Mamal = mongoose.model "Mamal", MamalSchema

checkIndex = () ->
  elasticMongooseQ.indexExists()

removeIndex = (bool) ->
  if bool
    elasticMongooseQ.deleteIndex()
  else
    Q()

createIndex = () ->
  elasticMongooseQ.connect()

putMappings = ->
  tasks = [
    Fish.putMapping()
    Mamal.putMapping()
  ]
  Q.all(tasks)

addAnimals = ->
  fish1 = new Fish name: 'Whale', description: 'The biggest fish like thing in the sea'
  fish2 = new Fish name: 'Sail fish', description: 'A fast fish'
  fish3 = new Fish name: 'Great white shark', description: "It'll get ya!"
  mamal1 = new Mamal name: 'Monkey', description: 'Cheeky chappy'
  mamal2 = new Mamal name: 'Squirrel', description: 'Known for climbing trees and trumping'

  tasks = [
    fish1.saveQ()
    fish2.saveQ()
    fish3.saveQ()
    mamal1.saveQ()
    mamal2.saveQ()
  ]
  Q.all(tasks)


#
# Start tests
#
before (done) ->
  helper.mongo.connect()
  # Create fresh elasticsearch index
  Q.delay(1000).then(checkIndex).then(removeIndex).then(createIndex).then(putMappings).nodeify(done)


describe 'ElasticMongooseQ', ->

  context "Index", ->
    describe 'putMapping', ->
      it 'should put the mapping to the index', (done) ->
        Fish.putMapping().then (resp) ->
          expect(resp.acknowledged).to.equal(true)
        .nodeify(done)

      it 'should put the mapping to the index', (done) ->
        Mamal.putMapping().then (resp) ->
          expect(resp.acknowledged).to.equal(true)
        .nodeify(done)

  context "Collections", ->

    describe "syncSearchIndex", ->
      @timeout(15000)
      before (done) ->
        addAnimals().then(checkIndex).then(removeIndex).delay(500).then(createIndex).delay(500).then(putMappings).delay(500).nodeify(done)

      it 'should index all Fish documents', (done) ->
        Fish.syncSearchIndex().nodeify(done)

      it 'should index all Mamal documents', (done) ->
        Mamal.syncSearchIndex().nodeify(done)

    describe "search", ->
      @timeout(15000)

      before (done) ->
        checkIndex().then(removeIndex).delay(500).then(createIndex).delay(500).then(putMappings).then(addAnimals).delay(500).nodeify(done)


      it 'should find 2 documents with the keyword fish', (done) ->
        promise = Fish.search
          query:
            match:
              description: 'fish'

        promise.then (results) ->
          expect(results.hits.hits).to.have.length(2)
        .nodeify(done)

      it 'should find 1 mamal with the keyword tree (stemmed trees)', (done) ->
        promise = Mamal.search
          query:
            match:
              description: 'tree'

        promise.then (results) ->
          expect(results.hits.hits).to.have.length(1)
        .nodeify(done)

  context "Documents", ->
    elephant = new Mamal name: 'Elephant', description: 'The biggest mamal not in the sea'

    saveElephant = () ->
      elephant.saveQ()

    findElephant = (elephant) ->
      elasticClient.get
        index: elasticIndex
        type: 'mamal'
        id: "#{elephant.id}"

    checkElephantInIndex = (resp) ->
      expect(resp._id).to.equal(elephant.id)
      Q(true)

    removeElephant = () ->
      elephant.remove()

    checkElephantNotInIndex = (err) ->
      expect(err.message).to.equal('Not Found')

    describe 'test model', ->
      it 'should have the name "Elephant"', (done) ->
        expect(elephant).to.have.property('name')
        expect(elephant.name).to.equal('Elephant')
        done()

    describe 'when saving the elephant', ->
      it 'should also save to elasticsearch', (done) ->
        saveElephant().then(findElephant).then(checkElephantInIndex).nodeify(done)

    describe 'when removing a document', ->
      it 'should also remove from elasticsearch', (done) ->
        saveElephant()
          .then(findElephant)
          .then(checkElephantInIndex)
          .then(removeElephant)
          .then(findElephant)
          .catch(checkElephantNotInIndex)
          .nodeify(done)
