'use strict'

const async = require('async')
const Boom = require('boom')

const config = require('./../lib/config')
const authConfig = require('./../lib/config/config.auth')
const userOps = require('./../lib/ops/userOps')
const authorizeOps = require('./../lib/ops/authorizeOps')

function isSuperUser (user) {
  return user.organizationId === config.get('authorization.superUser.organization.id')
}

function loadUser (job, next) {
  const { userId } = job

  userOps.getUserOrganizationId(userId, (err, organizationId) => {
    if (err) return next(Boom.unauthorized('Bad credentials'))

    userOps.readUser({ id: userId, organizationId }, (err, user) => {
      if (err) return next(Boom.unauthorized('Bad credentials'))
      job.currentUser = user

      next()
    })
  })
}

function impersonate (job, next) {
  const { currentUser } = job
  job.organizationId = currentUser.organizationId

  if (isSuperUser(currentUser) && job.requestedOrganizationId) {
    job.organizationId = job.requestedOrganizationId
  }

  next()
}

function checkAuthorization (userId, action, resource, done) {
  const params = { userId, action, resource }

  authorizeOps.isUserAuthorized(params, (err, result) => {
    if (err) return done(err)
    done(null, result.access)
  })
}

function buildResourcesForUser (builder, userId, organizationId, done) {
  const buildParams = {
    userId,
    teamId: '*',
    organizationId
  }

  const resources = [builder(buildParams)]

  userOps.readUser({ id: userId, organizationId: organizationId }, (err, user) => {
    if (err && err.output.statusCode === 404) return done(null, resources)
    if (err) return done(err)

    user.teams.forEach((team) => {
      buildParams.teamId = team.id
      resources.push(builder(buildParams))
    })

    done(null, resources)
  })
}

function buildResources (plugin, request, organizationId, done) {
  let resource = plugin.resource

  if (resource) {
    return done(null, [resource])
  }

  const resourceType = request.route.path.split('/')[2]
  const resourceBuilder = authConfig.resources[resourceType]

  if (!resourceBuilder) {
    return done({ error: 'Resource builder not found' })
  }

  const requestParams = plugin.getParams ? plugin.getParams(request) : {}
  const buildParams = Object.assign({}, { organizationId }, requestParams)

  if (resourceType === 'users' && buildParams.userId) {
    return buildResourcesForUser(resourceBuilder, buildParams.userId, organizationId, done)
  }

  done(null, [resourceBuilder(buildParams)])
}

function authorize (job, next) {
  if (!job.plugin) return next(Boom.forbidden('Invalid credentials', 'udaru'))

  buildResources(job.plugin, job.request, job.organizationId, (err, resources) => {
    if (err) return Boom.unauthorized('Bad credentials')

    const action = job.plugin.action
    const userId = job.currentUser.id

    async.any(resources, async.apply(checkAuthorization, userId, action), (err, valid) => {
      if (err) return next(Boom.forbidden('Invalid credentials', 'udaru'))
      if (!valid) return next(Boom.forbidden('Invalid credentials', 'udaru'))

      next()
    })
  })
}

module.exports = (options, server, request, userId, callback) => {

  const job = {
    userId,
    request,
    requestedOrganizationId: request.headers.org,
    plugin: request.route.settings.plugins && request.route.settings.plugins.auth
  }

  async.applyEachSeries([
    loadUser,
    impersonate,
    authorize
  ], job, (err) => {
    if (err) return callback(err)
    callback(null, {
      user: job.currentUser,
      organizationId: job.organizationId
    })
  })
}

@@ -284,27 +284,30 @@
 unction 
-isSuperUser
+canImpersonate
  (user) 
@@ -906,19 +906,22 @@
 if (
-isSuperUser
+canImpersonate
 (cur
@@ -1835,22 +1835,26 @@
 ources (
-plugin
+authParams
 , reques
@@ -1897,22 +1897,26 @@
 ource = 
-plugin
+authParams
 .resourc
@@ -2139,17 +2139,18 @@
 one(
-%7B e
+new E
 rror
-: 
+(
 'Res
@@ -2173,18 +2173,17 @@
 t found'
- %7D
+)
 )%0A  %7D%0A%0A 
@@ -2205,22 +2205,26 @@
 arams = 
-plugin
+authParams
 .getPara
@@ -2228,22 +2228,26 @@
 arams ? 
-plugin
+authParams
 .getPara
@@ -2577,88 +2577,8 @@
 ) %7B%0A
-  if (!job.plugin) return next(Boom.forbidden('Invalid credentials', 'udaru'))%0A%0A
   bu
@@ -2594,22 +2594,26 @@
 ces(job.
-plugin
+authParams
 , job.re
@@ -2742,22 +2742,26 @@
 n = job.
-plugin
+authParams
 .action%0A
@@ -3139,16 +3139,201 @@
 ck) =%3E %7B
+%0A  const authParams = request.route.settings.plugins && request.route.settings.plugins.auth%0A%0A  if (!authParams) %7B%0A    return callback(Boom.forbidden('Invalid credentials', 'udaru'))%0A  %7D
 %0A%0A  cons
@@ -3367,16 +3367,32 @@
 equest,%0A
+    authParams,%0A
     requ
@@ -3435,91 +3435,8 @@
 .org
-,%0A    plugin: request.route.settings.plugins && request.route.settings.plugins.auth
 %0A  %7D

fix pr feedback and change some names