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 nonExistentCveId = 'CVE-2017-35437'
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 NullUserRepo {
  async getUserUUID () {
    return null
  }

  async findOneByUserNameAndOrgUUID () {
    return null
  }

  async isAdmin () {
    return null
  }
}

class NullOrgRepo {
  async findOneByShortName () {
    return null
  }

  async updateByOrgUUID () {
    return null
  }

  async getOrgUUID () {
    return null
  }

  async isSecretariat () {
    return null
  }

  async isSecretariatUUID () {
    return null
  }
}

describe('Testing the GET /cve-id/:id endpoint in CveId Controller', () => {
  context('Negative Tests', () => {
    it('Cve does not exist', (done) => {
      class CveIdGetCveIdDoesntExist {
        async aggregate () {
          return []
        }
      }

      app.route('/cve-id-doesnt-exist/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdDoesntExist() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new NullOrgRepo() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      chai.request(app)
        .get(`/cve-id-doesnt-exist/${nonExistentCveId}`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

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

    it('Cve is available', (done) => {
      class CveIdGetCveIdAvailable {
        async aggregate () {
          return [cveIdFixtures.cveDummy5]
        }
      }

      app.route('/cve-id-available/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdAvailable() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new NullOrgRepo() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      chai.request(app)
        .get(`/cve-id-available/${cveIdFixtures.cveDummy5.cve_id}`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(404)
          expect(res).to.have.property('body').and.to.be.a('object')
          const errObj = error.cveIdNotFound(cveIdFixtures.cveDummy5.cve_id)
          expect(res.body.error).to.equal(errObj.error)
          expect(res.body.message).to.equal(errObj.message)
          done()
        })
    })

    it('Requester is not a user of the same org or the secretariat and the cve is RESERVED', (done) => {
      class CveIdGetCveIdNotOwningOrg {
        async aggregate () {
          return [cveIdFixtures.cveReserved]
        }
      }

      class OrgGetCveIdNotOwnerOrgReserved {
        async getOrgUUID () {
          return cveIdFixtures.org.UUID
        }

        async isSecretariatUUID () {
          return false
        }
      }

      app.route('/cve-id-not-owning-org-reserved/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdNotOwningOrg() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgGetCveIdNotOwnerOrgReserved() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      chai.request(app)
        .get(`/cve-id-not-owning-org-reserved/${cveIdFixtures.cveId}`)
        .set(cveIdFixtures.orgHeader) // not the owning org or secretariat
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(200)
          expect(res).to.have.property('body').and.to.be.a('object')
          expect(res.body).to.have.property('cve_id').and.to.equal(cveIdFixtures.cveId)
          expect(res.body).to.have.property('state').and.to.equal('RESERVED')
          expect(res.body).to.have.property('owning_cna').and.to.equal('[REDACTED]')
          done()
        })
    })
  })

  context('Positive Tests', () => {
    it('Requester is not a user of the same org or the secretariat and the cve is not RESERVED', async () => {
      class CveIdGetCveIdNotOwningOrgNotReserved {
        constructor () {
          this.testRes1 = JSON.parse(JSON.stringify(cveIdFixtures.cveRejected))
          this.testRes1.owning_cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.user = cveIdFixtures.owningOrgUser.username
        }

        async aggregate () {
          return [this.testRes1]
        }
      }

      class OrgGetCveIdNotOwnerOrgNotReserved {
        async getOrgUUID () {
          return cveIdFixtures.org.UUID
        }

        async isSecretariatUUID () {
          return false
        }

        async findOneByUUID () {
          return cveIdFixtures.owningOrg
        }
      }

      app.route('/cve-id-not-owning-org-not-reserved/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdNotOwningOrgNotReserved() },
            getUserRepository: () => { return new NullUserRepo() },
            getOrgRepository: () => { return new OrgGetCveIdNotOwnerOrgNotReserved() }
          }
          req.ctx.repositories = factory
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      const res = await chai.request(app)
        .get(`/cve-id-not-owning-org-not-reserved/${cveIdFixtures.cveId}`)
        .set(cveIdFixtures.orgHeader) // not the owning cna or secretariat

      expect(res).to.have.status(200)
      expect(res).to.have.property('body').and.to.be.a('object')
      expect(res.body).to.have.property('cve_id').and.to.equal(cveIdFixtures.cveId)
      expect(res.body).to.have.property('state').and.to.equal('REJECTED')
      expect(res.body).to.have.property('owning_cna').and.to.equal(cveIdFixtures.owningOrg.short_name)
    })

    it('Requester is a user of the same org', async () => {
      class CveIdGetCveIdOwningOrg {
        constructor () {
          this.testRes1 = JSON.parse(JSON.stringify(cveIdFixtures.cvePublished))
          this.testRes1.owning_cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.user = cveIdFixtures.owningOrgUser.username
        }

        async aggregate () {
          return [this.testRes1]
        }
      }

      class UserGetCveIdOwningOrg {
        async findOneByUUID () {
          return cveIdFixtures.owningOrgUser
        }
      }

      class OrgGetCveIdOwningOrg {
        async getOrgUUID () {
          return cveIdFixtures.owningOrg.UUID
        }

        async isSecretariatUUID () {
          return false
        }

        async findOneByUUID () {
          return cveIdFixtures.owningOrg
        }
      }

      app.route('/cve-id-owning-org/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdOwningOrg() },
            getUserRepository: () => { return new UserGetCveIdOwningOrg() },
            getOrgRepository: () => { return new OrgGetCveIdOwningOrg() }
          }
          req.ctx.repositories = factory
          req.ctx.authenticated = true
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      const res = await chai.request(app)
        .get(`/cve-id-owning-org/${cveIdFixtures.cveId}`)
        .set(cveIdFixtures.owningOrgHeader)

      expect(res).to.have.status(200)
      expect(res).to.have.property('body').and.to.be.a('object')
      expect(res.body).to.have.property('cve_id').and.to.equal(cveIdFixtures.cveId)
      expect(res.body).to.have.property('state').and.to.equal('PUBLISHED')
      expect(res.body).to.have.property('owning_cna').and.to.equal(cveIdFixtures.owningOrg.short_name)
      expect(res.body).to.have.nested.property('requested_by.cna').and.to.equal(cveIdFixtures.owningOrg.short_name)
      expect(res.body).to.have.nested.property('requested_by.user').and.to.equal(cveIdFixtures.owningOrgUser.username)
    })

    it('Requester is the secretariat', (done) => {
      class CveIdGetCveIdOwningOrg {
        constructor () {
          this.testRes1 = JSON.parse(JSON.stringify(cveIdFixtures.cvePublished))
          this.testRes1.owning_cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.cna = cveIdFixtures.owningOrg.short_name
          this.testRes1.requested_by.user = cveIdFixtures.owningOrgUser.username
        }

        async aggregate () {
          return [this.testRes1]
        }
      }

      class UserGetCveIdRequestorSecretariat {
        async findOneByUUID (uuid) {
          if (uuid === cveIdFixtures.secretariatUser.UUID) {
            return cveIdFixtures.secretariatUser
          } else {
            return cveIdFixtures.owningOrgUser
          }
        }
      }

      class OrgGetCveIdRequestorSecretariat {
        async getOrgUUID () {
          return cveIdFixtures.secretariatOrg.UUID
        }

        async isSecretariatUUID () {
          return true
        }

        async findOneByUUID (uuid) {
          if (uuid === cveIdFixtures.secretariatOrg.UUID) {
            return cveIdFixtures.secretariatOrg
          } else {
            return cveIdFixtures.owningOrg
          }
        }
      }

      app.route('/cve-id-is-secretariat/:id')
        .get((req, res, next) => {
          const factory = {
            getCveIdRepository: () => { return new CveIdGetCveIdOwningOrg() },
            getUserRepository: () => { return new UserGetCveIdRequestorSecretariat() },
            getOrgRepository: () => { return new OrgGetCveIdRequestorSecretariat() }
          }
          req.ctx.repositories = factory
          req.ctx.authenticated = true
          next()
        }, cveIdParams.parseGetParams, cveIdController.CVEID_GET_SINGLE)

      chai.request(app)
        .get(`/cve-id-is-secretariat/${cveIdFixtures.cveId}`)
        .set(cveIdFixtures.secretariatHeader)
        .end((err, res) => {
          if (err) {
            done(err)
          }

          expect(res).to.have.status(200)
          expect(res).to.have.property('body').and.to.be.a('object')
          expect(res.body).to.have.property('cve_id').and.to.equal(cveIdFixtures.cveId)
          expect(res.body).to.have.property('state').and.to.equal('PUBLISHED')
          expect(res.body).to.have.property('owning_cna').and.to.equal(cveIdFixtures.owningOrg.short_name)
          expect(res.body).to.have.nested.property('requested_by.cna').and.to.equal(cveIdFixtures.owningOrg.short_name)
          expect(res.body).to.have.nested.property('requested_by.user').and.to.equal(cveIdFixtures.owningOrgUser.username)
          done()
        })
    })
  })
})
