var _ = require('lodash'),

    HEADER = 'header',
    QUERY_PARAMS = 'queryParams',
    BEARER = 'bearer',
    MAC = 'mac',
    AUTHORIZATION = 'Authorization',
    ACCESS_TOKEN = 'access_token',
    AUTHORIZATION_PREFIX = 'Bearer',
    OAUTH2_PARAMETERS = [
        'accessToken',
        'addTokenTo',
        'tokenType',
        'headerPrefix'
    ];

/**
 * @implements {AuthHandlerInterface}
 */
module.exports = {
    /**
     * @property {AuthHandlerInterface~AuthManifest}
     */
    manifest: {
        info: {
            name: 'oauth2',
            version: '1.0.0'
        },
        updates: [
            {
                property: AUTHORIZATION,
                type: 'header'
            },
            {
                property: ACCESS_TOKEN,
                type: 'url.param'
            }
        ]
    },

    /**
     * Initializes an item (extracts parameters from intermediate requests if any, etc)
     * before the actual authorization step.
     *
     * @param {AuthInterface} auth
     * @param {Response} response
     * @param {AuthHandlerInterface~authInitHookCallback} done
     */
    init: function (auth, response, done) {
        done(null);
    },

    /**
     * Verifies whether the request has valid basic auth credentials (which is always).
     * Sanitizes the auth parameters if needed.
     *
     * @param {AuthInterface} auth
     * @param {AuthHandlerInterface~authPreHookCallback} done
     */
    pre: function (auth, done) {
        done(null, Boolean(auth.get('accessToken')));
    },

    /**
     * Verifies whether the basic auth succeeded.
     *
     * @param {AuthInterface} auth
     * @param {Response} response
     * @param {AuthHandlerInterface~authPostHookCallback} done
     */
    post: function (auth, response, done) {
        done(null, true);
    },

    /**
     * Signs a request.
     *
     * @param {AuthInterface} auth
     * @param {Request} request
     * @param {AuthHandlerInterface~authSignHookCallback} done
     */
    sign: function (auth, request, done) {
        var params = auth.get(OAUTH2_PARAMETERS),
            tokenType;

        // Validation
        if (!params.accessToken) {
            return done(); // Nothing to do if required parameters are not present.
        }

        // Defaults
        params.addTokenTo = params.addTokenTo || HEADER; // Add token to header by default
        params.tokenType = params.tokenType || BEARER; // Use `Bearer` token type by default
        params.headerPrefix = _.isNil(params.headerPrefix) ?
            AUTHORIZATION_PREFIX : _.trim(String(params.headerPrefix));

        // add a space after prefix only if there is any prefix
        params.headerPrefix && (params.headerPrefix += ' ');

        // Some servers send 'Bearer' while others send 'bearer'
        tokenType = _.toLower(params.tokenType);

        // @TODO Add support for HMAC
        if (tokenType === MAC) {
            return done();
        }

        // treat every token types (other than MAC) as bearer token

        // clean conflicting headers and query params
        // @todo: we should be able to get conflicting params from auth manifest
        // and clear them before the sign step for any auth
        request.removeHeader(AUTHORIZATION, {ignoreCase: true});
        request.removeQueryParams([ACCESS_TOKEN]);

        if (params.addTokenTo === QUERY_PARAMS) {
            request.addQueryParams({
                key: ACCESS_TOKEN,
                value: params.accessToken,
                system: true
            });
        }
        else if (params.addTokenTo === HEADER) {
            request.addHeader({
                key: AUTHORIZATION,
                value: params.headerPrefix + params.accessToken,
                system: true
            });
        }

        return done();
    }
};
