require '../common'

campaignURL = getURL('/db/campaign')
campaignSchema = require '../../../app/schemas/models/campaign.schema'
campaignLevelProperties = _.keys(campaignSchema.properties.levels.additionalProperties.properties)
Achievement = require '../../../server/models/Achievement'
Campaign = require '../../../server/models/Campaign'
Level = require '../../../server/models/Level'
User = require '../../../server/models/User'
request = require '../request'
utils = require '../utils'
slack = require '../../../server/slack'
Promise = require 'bluebird'


describe 'GET /db/campaign', ->

  beforeEach utils.wrap (done) ->
    yield utils.clearModels([Campaign])
    @heroCampaign1 = yield new Campaign({name: 'Hero Campaign 1', type: 'hero'}).save()
    @heroCampaign2 = yield new Campaign({name: 'Hero Campaign 2', type: 'hero'}).save()
    @courseCampaign1 = yield new Campaign({name: 'Course Campaign 1', type: 'course'}).save()
    @courseCampaign2 = yield new Campaign({name: 'Course Campaign 2', type: 'course'}).save()
    done()

  it 'returns all campaigns', utils.wrap (done) ->
    [res, body] =  yield request.getAsync getURL('/db/campaign'), { json: true }
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(4)
    done()
    
  it 'accepts project parameter with [] notation', utils.wrap (done) ->
    [res, body] =  yield request.getAsync getURL('/db/campaign?project[]=name&project[]=type'), { json: true }
    expect(res.statusCode).toBe(200)
    expect(res.body[0].slug).toBeUndefined()
    done()
    
  describe 'with GET query param type', ->
    it 'returns campaigns of that type', utils.wrap (done) ->
      [res, body] =  yield request.getAsync getURL('/db/campaign?type=course'), { json: true }
      expect(res.statusCode).toBe(200)
      expect(body.length).toBe(2)
      for campaign in body
        expect(campaign.type).toBe('course')
      done()


describe 'POST /db/campaign', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    @levels = (level.toObject() for level in [yield utils.makeLevel(), yield utils.makeLevel()])
    done()

  it 'can create campaigns', utils.wrap (done) ->
    campaign = {
      levels: {}
    }
    for level in @levels.reverse()
      campaign.levels[level.original.valueOf()] = _.pick level, campaignLevelProperties
    [res, body] = yield request.postAsync {uri: campaignURL, json: campaign}
    expect(res.statusCode).toBe(201)
    campaign = body
    done()

describe 'PUT /db/campaign/:handle', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Achievement, Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    @campaign = yield utils.makeCampaign()
    @levelsUpdated = @campaign.get('levelsUpdated').toISOString()
    done()
  
  it 'saves changes to campaigns', utils.wrap (done) ->
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
    expect(body.name).toBe('A new name')
    c = yield Campaign.findById(body._id)
    expect(c.get('name')).toBe('A new name')
    done()
    
  it 'does not allow normal users to make changes', utils.wrap (done) ->
    user = yield utils.initUser()
    yield utils.loginUser(user)
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
    expect(res.statusCode).toBe(403)
    done()
    
  it 'allows normal users to put translation changes', utils.wrap (done) ->
    user = yield utils.initUser()
    yield utils.logout()
    yield utils.loginUser(user)
    json = _.clone @campaign.toObject()
    json.i18n = { de: { name: 'A new name' } }
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: json }
    expect(res.statusCode).toBe(200)
    done()
    
  it 'sends a slack message', utils.wrap (done) ->
    spyOn(slack, 'sendSlackMessage')
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
    expect(slack.sendSlackMessage).toHaveBeenCalled()
    done()
    
  it 'sets campaign.levelsUpdated to now iff levels are changed', utils.wrap (done) ->
    data = {name: 'whatever'}
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: data }
    expect(body.levelsUpdated).toBe(@levelsUpdated)
    yield new Promise((resolve) -> setTimeout(resolve, 10))
    data = {levels: {'a': {original: 'a'}}}
    [res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: data }
    expect(body.levelsUpdated).not.toBe(@levelsUpdated)
    done()
    
    
describe 'GET, POST /db/campaign/names', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Achievement, Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    @campaignA = yield utils.makeCampaign()
    @campaignB = yield utils.makeCampaign()
    done()
    
  it 'returns names of campaigns by for given ids', utils.wrap (done) ->
    [res, body] = yield request.getAsync({url: getURL("/db/campaign/names?ids=#{@campaignA.id},#{@campaignB.id}"), json: true})
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(2)
    [res, body] = yield request.postAsync({url: getURL('/db/campaign/names'), json: { ids: [@campaignA.id, @campaignB.id] }})
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(2)
    done()
    

describe 'GET /db/campaign/:handle/levels', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    @level1 = yield utils.makeLevel()
    @level2 = yield utils.makeLevel()
    @campaign = yield utils.makeCampaign({}, {levels: [@level1, @level2]})
    done()
    
  it 'fetches the levels in a campaign', utils.wrap (done) ->
    url = getURL("/db/campaign/#{@campaign._id}/levels")
    [res, body] = yield request.getAsync {uri: url, json: true}
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(2)
    expect(_.difference([@level1.get('slug'), @level2.get('slug')], _.pluck(body, 'slug')).length).toBe(0)
    done()

describe 'GET /db/campaign/:handle/achievements', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Achievement, Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    level = yield utils.makeLevel()
    @achievement = yield utils.makeAchievement({}, {related: level})
    @campaign = yield utils.makeCampaign({}, {levels: [level]})
    done()
  
  it 'fetches the achievements in the levels in a campaign', utils.wrap (done) ->
    url = getURL("/db/campaign/#{@campaign.id}/achievements")
    [res, body] = yield request.getAsync {uri: url, json: true}
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(1)
    done()

describe 'GET /db/campaign/-/overworld', ->
  beforeEach utils.wrap (done) ->
    yield utils.clearModels [Campaign, Level, User]
    admin = yield utils.initAdmin()
    yield utils.loginUser(admin)
    level = yield utils.makeLevel()
    @campaignA = yield utils.makeCampaign({type: 'hero', hidesHUD: true})
    @campaignB = yield utils.makeCampaign({type: 'hero'}, {
      levels: [level]
      adjacentCampaigns: [@campaignA]
    })
    @campaignC = yield utils.makeCampaign({type: 'course'})
    done()

  it 'fetches campaigns of type "hero", returning projected level and adjacentCampaign children', utils.wrap (done) ->
    url = getURL("/db/campaign/-/overworld")
    [res, body] = yield request.getAsync {uri: url, json: true}
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(2)
    for campaign in body
      expect(campaign.type).toBe('hero')
      
    campaign = _.findWhere(body, {_id: @campaignB.id})
    expect(_.size(campaign.levels)).toBeGreaterThan(0)
    for level in _.values(campaign.levels)
      expect(level.slug).toBeDefined()
    expect(_.size(campaign.adjacentCampaigns)).toBeGreaterThan(0)
    for campaign in _.values(campaign.adjacentCampaigns)
      expect(campaign.name).toBeDefined()
    done()

  it 'takes a project query param', utils.wrap (done) ->
    url = getURL("/db/campaign/-/overworld?project=name")
    [res, body] = yield request.getAsync {uri: url, json: true}
    expect(res.statusCode).toBe(200)
    expect(body.length).toBe(2)
    for campaign in body
      expect(campaign.type).toBeUndefined()
      expect(campaign.name).toBeDefined()
    done()
