const defaults = require('lodash.defaults');
const xhr = require('xhr');

const jar = require('./jar');
const log = require('./log');
const urlParams = require('./url-params');

/**
 * Helper method that constructs requests to the scratch api.
 * Custom arguments:
 *     - useCsrf [boolean] – handles unique csrf token retrieval for POST requests. This prevents
 *       CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
 *
 * It also takes in other arguments specified in the xhr library spec.
 *
 * @param  {object}   opts     optional xhr args (see above)
 * @param  {Function} callback [description]
 */
module.exports = (opts, callback) => {
    defaults(opts, {
        host: process.env.API_HOST,
        headers: {},
        responseType: 'json',
        useCsrf: false
    });

    if (opts.host === '') {
        defaults(opts.headers, {
            'X-Requested-With': 'XMLHttpRequest'
        });
    }

    opts.uri = opts.host + opts.uri;

    if (opts.params) {
        opts.uri = [opts.uri, urlParams(opts.params)]
            .join(opts.uri.indexOf('?') === -1 ? '?' : '&');
    }

    if (opts.formData) {
        opts.body = urlParams(opts.formData);
        opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
    }

    const apiRequest = options => {
        if (options.host !== '') {
            if ('withCredentials' in new XMLHttpRequest()) {
                options.useXDR = false;
            } else {
                // For IE < 10, we must use XDR for cross-domain requests. XDR does not support
                // custom headers.
                options.useXDR = true;
                delete options.headers;
                if (options.authentication) {
                    const authenticationParams = [`x-token=${options.authentication}`];
                    const parts = options.uri.split('?');
                    const qs = (parts[1] || '')
                        .split('&')
                        .concat(authenticationParams)
                        .join('&');
                    options.uri = `${parts[0]}?${qs}`;
                }
            }
        }
        xhr(options, (err, res, body) => {
            if (err) log.error(err);
            if (options.responseType === 'json' && typeof body === 'string') {
                // IE doesn't parse responses as JSON without the json attribute,
                // even with responseType: 'json'.
                // See https://github.com/Raynos/xhr/issues/123
                try {
                    body = JSON.parse(body);
                } catch (e) {
                    // Not parseable anyway, don't worry about it
                }
            }
            // Legacy API responses come as lists, and indicate to redirect the client like
            // [{success: true, redirect: "/location/to/redirect"}]
            try {
                if ('redirect' in body[0]) window.location = body[0].redirect;
            } catch (e) {
                // do nothing
            }
            callback(err, body, res);
        });
    };

    if (typeof jar.get('scratchlanguage') !== 'undefined') {
        opts.headers['Accept-Language'] = `${jar.get('scratchlanguage')}, en;q=0.8`;
    }
    if (opts.authentication) {
        opts.headers['X-Token'] = opts.authentication;
    }
    if (opts.useCsrf) {
        jar.use('scratchcsrftoken', '/csrf_token/', (err, csrftoken) => {
            if (err) return log.error('Error while retrieving CSRF token', err);
            opts.headers['X-CSRFToken'] = csrftoken;
            apiRequest(opts);
        });
    } else {
        apiRequest(opts);
    }
};