/**
 * Module dependencies
 */

var url = require('url')
var revalidator = require('revalidator')
var settings = require('../boot/settings')
var mailer = require('../boot/mailer').getMailer()
var User = require('../models/User')
var OneTimeToken = require('../models/OneTimeToken')
var PasswordsDisabledError = require('../errors/PasswordsDisabledError')

/**
 * Account Recovery
 *
 * Recovery flow:
 *
 * 1. Accept user e-mail
 * 2. Validate input as valid e-mail, display error if not
 * 3. Verify user account exists with specified e-mail
 * 4. If user account exists, issue token and send e-mail
 * 5. Regardless of success/failure, direct user to emailSent view
 *
 * Password reset flow:
 *
 * 1. When user clicks on link, verify token exists and is for pw reset
 * 2. Direct user to resetPassword view
 * 3. Verify password fulfills password strength requirements
 * 4. If error, re-display resetPassword view, but with error message
 * 5. Update user object and revoke token
 * 6. Send user an e-mail notifying them that their password was changed
 * 7. Direct user to passwordReset view
 *
 * Default expiry time for tokens is 1 day
 */

function verifyPasswordsEnabled (req, res, next) {
  if (!settings.providers.password) {
    return next(new PasswordsDisabledError())
  } else {
    return next()
  }
}

function verifyMailerConfigured (req, res, next) {
  if (!mailer.transport) {
    return next(new Error('Mailer not configured.'))
  } else {
    return next()
  }
}

function verifyPasswordResetToken (req, res, next) {
  if (!req.query.token) {
    return res.render('recovery/resetPassword', {
      error: 'Invalid reset code.'
    })
  }

  OneTimeToken.peek(req.query.token, function (err, token) {
    if (err) { return next(err) }
    if (!token || token.use !== 'resetPassword') {
      return res.render('recovery/resetPassword', {
        error: 'Invalid reset code.'
      })
    }

    req.passwordResetToken = token
    next()
  })
}

module.exports = function (server) {
  server.get('/recovery',
    verifyPasswordsEnabled,
    verifyMailerConfigured,
    function (req, res, next) {
      res.render('recovery/start')
    }
  )

  server.post('/recovery',
    verifyPasswordsEnabled,
    verifyMailerConfigured,
    function (req, res, next) {
      if (
        !req.body.email ||
        !revalidator.validate.formats.email.test(req.body.email)
      ) {
        return res.render('recovery/start', {
          error: 'Please enter a valid e-mail address.'
        })
      }

      User.getByEmail(req.body.email, function (err, user) {
        if (err) { return next(err) }
        if (!user) { return res.render('recovery/emailSent') }

        OneTimeToken.issue({
          sub: user._id,
          ttl: 3600 * 24, // 1 day
          use: 'resetPassword'
        }, function (err, token) {
          if (err) { return next(err) }

          var resetPasswordURL = url.parse(settings.issuer)
          resetPasswordURL.pathname = 'resetPassword'
          resetPasswordURL.query = { token: token._id }

          mailer.sendMail('resetPassword', {
            email: user.email,
            resetPasswordURL: url.format(resetPasswordURL)
          }, {
            to: user.email,
            subject: 'Reset your password'
          }, function (err, responseStatus) {
            if (err) { }
            // TODO: REQUIRES REFACTOR TO MAIL QUEUE
            res.render('recovery/emailSent')
          })
        })
      })
    }
  )

  server.get('/resetPassword',
    verifyPasswordsEnabled,
    verifyMailerConfigured,
    verifyPasswordResetToken,
    function (req, res, next) {
      res.render('recovery/resetPassword')
    }
  )

  server.post('/resetPassword',
    verifyPasswordsEnabled,
    verifyMailerConfigured,
    verifyPasswordResetToken,
    function (req, res, next) {
      var uid = req.passwordResetToken.sub

      if (req.body.password !== req.body.confirmPassword) {
        return res.render('recovery/resetPassword', {
          validationError: 'Passwords do not match.'
        })
      }

      User.changePassword(uid, req.body.password, function (err, user) {
        if (err && (
          err.name === 'PasswordRequiredError' ||
          err.name === 'InsecurePasswordError'
        )) {
          return res.render('recovery/resetPassword', {
            validationError: err.message
          })
        }

        if (err) { return next(err) }

        OneTimeToken.revoke(req.passwordResetToken._id, function (err) {
          if (err) { return next(err) }

          var recoveryURL = url.parse(settings.issuer)
          recoveryURL.pathname = 'recovery'

          mailer.sendMail('passwordChanged', {
            email: user.email,
            recoveryURL: url.format(recoveryURL)
          }, {
            to: user.email,
            subject: 'Your password has been changed'
          }, function (err, responseStatus) {
            if (err) { }
            // TODO: REQUIRES REFACTOR TO MAIL QUEUE
            res.render('recovery/passwordReset')
          })
        })
      })
    }
  )
}
