const express = require('express')
const app = express()
const chai = require('chai')
const expect = chai.expect
chai.use(require('chai-http'))

// Body Parser Middleware
app.use(express.json()) // Allows us to handle raw JSON data
app.use(express.urlencoded({ extended: false })) // Allows us to handle url encoded data
const middleware = require('../../../../src/middleware/middleware')
app.use(middleware.createCtxAndReqUUID)

const year20 = 2020
const getConstants = require('../../../../src/constants').getConstants
const errors = require('../../../../src/controller/cve-id.controller/error')
const error = new errors.CveIdControllerError()

const cveIdFixtures = require('../mockObjects.cve-id')
const cveIdController = require('../../../../src/controller/cve-id.controller/cve-id.controller')
const cveIdParams = require('../../../../src/controller/cve-id.controller/cve-id.middleware')

class NullCveIdRepo {
  async countDocuments () {
    return null
  }
}

class NullUserRepo {
  async getUserUUID () {
    return null
  }

  async findOneByUserNameAndOrgUUID () {
    return null
  }

  async isAdmin () {
    return null
  }
}

class OrgReserveShortNameUndefined {
  async isSecretariat () {
    return true
  }
}

class OrgReserveAmountLargerThanNonSequential {
  async isSecretariat () {
    return true
  }

  async findOneByShortName () {
    return cveIdFixtures.org
  }

  async findOneAndUpdate (query) {
    if (query.short_name === cveIdFixtures.org.short_name && cveIdFixtures.org.inUse === false) {
      cveIdFixtures.org.inUse = true
      return cveIdFixtures.org
    } else if (query.short_name === cveIdFixtures.org.short_name && cveIdFixtures.org.inUse === true) {
      cveIdFixtures.org.inUse = false
      return null
    }
  }
}

describe('Testing the general logic of POST /cve-id endpoint in CveId Controller', () => {
  context('Negative Tests', () => {
    it('Requester is not a user of the same org or the secretariat', (done) => {
      class OrgReserveNotOwningOrg {
        async isSecretariat () {
          return false
        }
      }

      app.route('/cve-id-reserve-not-owning-org')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveNotOwningOrg() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-not-owning-org?cve_year=${year20}&amount=1`)
        .set(cveIdFixtures.orgHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(403)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.orgCannotReserveForOther()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Shortname query parameter is undefined', (done) => {
      app.route('/cve-id-reserve-shortname-undefined')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveShortNameUndefined() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-shortname-undefined?cve_year=${year20}&amount=1`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.noOrgShortName()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Year query parameter is undefined', (done) => {
      app.route('/cve-id-reserve-year-undefined')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveShortNameUndefined() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-year-undefined?short_name=${cveIdFixtures.owningOrg.short_name}&amount=1`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.noYear()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Amount query parameter is undefined', (done) => {
      app.route('/cve-id-reserve-amount-undefined')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveShortNameUndefined() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-amount-undefined?short_name=${cveIdFixtures.owningOrg.short_name}&cve_year=${year20}`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.noAmount()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Amount query parameter is <= 0', (done) => {
      app.route('/cve-id-reserve-amount-0')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveShortNameUndefined() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-amount-0?short_name=${cveIdFixtures.owningOrg.short_name}&cve_year=${year20}&amount=0`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.invalidAmount()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Amount query parameter is > 1 and batchType is undefined', (done) => {
      app.route('/cve-id-reserve-batchtype-undefined')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveShortNameUndefined() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-batchtype-undefined?short_name=${cveIdFixtures.owningOrg.short_name}&cve_year=${year20}&amount=5`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.noBatchType()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Org does not exist', (done) => {
      class OrgReserveOrgDoesntExist {
        async isSecretariat () {
          return true
        }

        async findOneByShortName () {
          return null
        }
      }

      app.route('/cve-id-reserve-org-doesnt-exist')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveOrgDoesntExist() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-org-doesnt-exist?short_name=${cveIdFixtures.nonExistentOrg.short_name}&cve_year=${year20}&amount=1`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(403)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.orgDne(cveIdFixtures.nonExistentOrg.short_name, 'short_name', 'query')
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Amount query parameter is greater than max nonsequential amount allowed', (done) => {
      const CONSTANTS = getConstants()

      app.route('/cve-id-reserve-amount-greater-nonsequential')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveAmountLargerThanNonSequential() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-amount-greater-nonsequential?short_name=${cveIdFixtures.org.short_name}&cve_year=${year20}&amount=${(CONSTANTS.NONSEQUENTIAL_MAX_AMOUNT + 1)}&batch_type=nonsequential`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(403)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.overNonSequentialMaxAmount()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Amount query parameter is > than id_quota', (done) => {
      class OrgReserveAmountGreaterIdQuota {
        async isSecretariat () {
          return true
        }

        async findOneByShortName () {
          return cveIdFixtures.org
        }
      }

      app.route('/cve-id-reserve-amount-greater-id-quota')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveAmountGreaterIdQuota() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-amount-greater-id-quota?short_name=${cveIdFixtures.org.short_name}&cve_year=${year20}&amount=700&batch_type=sequential`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(403)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.overIdQuota(res.body.details)
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          expect(res.body.details).to.equal(errObj.details)
          done()
        })
    })

    it('Invalid batchType', (done) => {
      app.route('/cve-id-reserve-invalid-batchtype')
        .post((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new NullCveIdRepo() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgReserveAmountLargerThanNonSequential() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parsePostParams, cveIdController.CVEID_RESERVE)

      chai.request(app)
        .post(`/cve-id-reserve-invalid-batchtype?short_name=${cveIdFixtures.org.short_name}&cve_year=${year20}&amount=1&batch_type=pair`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(400)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.invalidBatchType()
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })
  })
})
