text: 'use strict';
  
  var jwtDecode = require('jwt-decode');
  var VError = require('verror').VError;
  var headerUtil = require('../utils/header-util');
  var urlUtils = require('../utils/url-utils');
  var uaaUtils = require('../utils/uaa-utils');
  var request = require('request');
  var loggerUtils = require('../utils/logger');
  var tracer = loggerUtils.getTracer(__filename);
  var urijs = require('urijs');
  
  module.exports = {
    MAX_ATTEMPTS_NUMBER : 3,
    storeToken: function (req, options, done) {
      req.session.regenerate(function (err) {
        if (err) {
          return done(err);
        }
  
        req.session.user = module.exports.getUserProperties(options);
        req.session.save();
        done(null, req.session.user);
      });
    },
  
    getUserProperties: function (options) {
      var token = jwtDecode(options.accessToken);
      var userName = token.user_name;
      var scopes = [];
      if (options.scope)
      {
        scopes = options.scope.trim ? options.scope.trim().split(' ') : options.scope;
      }
      return {
        id: userName,
        name: userName || 'n/a', // 'n/a' is for passport
        token: {
          accessToken: options.accessToken,
          expiryDate: module.exports.getExpiresAt(options.expiresIn).getTime(),
          refreshToken: options.refreshToken,
          oauthOptions: options.oauthOptions
        },
        tenantid: token.zid,
        scopes: scopes,
        tenant: token.ext_attr && token.ext_attr.zdn
      };
    },
  
    getExpiresAt: function (duration) {
      // respect network latency if possible
      var expiresIn;
      if (duration > 180) {
        expiresIn = duration - 120;
      } else {
        expiresIn = duration;
      }
      return new Date(new Date().getTime() + expiresIn * 1000);
    },
  
    getCallBackUrl: function (req) {
      var appRouterUrl = urlUtils.buildAppRouterUrl(req);
      var loginCallback = req.routerConfig.appConfig.login.callbackEndpoint;
      var proxyPrefix = '';
  
      if (req.headers['x-forwarded-path']) {
        var toCutOut = req.url.length;
        var parsedUrl = urijs.parse(req.url);
        if (req.headers['x-forwarded-path'].indexOf(loginCallback) > -1 && parsedUrl.query) {
          toCutOut = loginCallback.length;
        }
        var parsedXForwardedPath = urijs.parse(req.headers['x-forwarded-path']);
        proxyPrefix = parsedXForwardedPath.path.substr(0,parsedXForwardedPath.path.length - toCutOut +
            (req.url[0] === '/' ? 1 : 0));
      }
      return urlUtils.join(appRouterUrl + proxyPrefix, loginCallback);
    },
  
    loadOauthOptions: function (req, cb) {
      uaaUtils.getUaaConfig(req, function (err, uaaOptions) {
        if (err) {
          return cb(err);
        }
  
        var uaaUrl = uaaOptions.url;
        var options = {
          authorizationURL: urlUtils.join(uaaUrl, '/oauth/authorize'),
          tokenURL: urlUtils.join(uaaUrl, '/oauth/token'),
          clientid: uaaOptions.clientid,
          clientsecret: uaaOptions.clientsecret,
          url: uaaUrl,
          callbackURL: module.exports.getCallBackUrl(req),
          customHeaders: {}
        };
  
        headerUtil.updateSapPassport(req.headers, options.customHeaders);
        cb(null, options);
      });
    },
  
    getAccessTokenCallback: function (strategy, req) {
      return function accessTokenCallback(err, result) {
        if (err) {
          var error = new VError(err, 'Could not obtain access token');
          error.status = err.status;
          return strategy.error(error);
        }
        function verified(err, user, info) {
          if (err) {
            return strategy.error(err);
          }
          if (!user) {
            return strategy.fail(info);
          }
          strategy.success(user, info);
        }
  
        try {
          strategy.verify(req, result, verified);
        } catch (ex) {
          return strategy.error(ex);
        }
      };
    },
  
    callUaa: function (requestOptions,shouldDoRequest, cb) {
      var requestStart = Date.now();
      var attemptsNum = module.exports.MAX_ATTEMPTS_NUMBER;
      function doRequest() {
        if (shouldDoRequest){
          var error = shouldDoRequest();
          if (error){
            return cb(error);
          }
  
        }
        request.post(requestOptions, function onResponse(err, res, body) {
          if (err || res.statusCode !== 200) {
            tracer.info(err, 'Request attempt %d to UAA at %s failed in %d ms.', module.exports.MAX_ATTEMPTS_NUMBER - attemptsNum + 1, requestOptions.url, Date.now() - requestStart);
            --attemptsNum;
            if (!attemptsNum) {
              if (res && res.statusCode !== 200){
                var error = new VError('unexpected response from UAA at %s: status code: %d, response body: "%s"',
                    requestOptions.url, res.statusCode, body);
                error.status = res.statusCode;
                return cb(error);
              }
              return cb(new VError(err,'Request to UAA failed'));
            }
            return doRequest();
          }
          try {
            var responseJSON = JSON.parse(body);
          } catch (err) {
            return cb(new VError(err, 'unexpected response from UAA. Expected json, got: "%s"', body));
          }
          cb(null, responseJSON);
        });
      }
      doRequest();
    }
  
  };
  