var App = App || {};

App.API = function(_, jQuery, promise, appState) {

    var baseUrl = '/api/';
    var AJAX_UNSENT = 0;
    var AJAX_OPENED = 1;
    var AJAX_HEADERS_RECEIVED = 2;
    var AJAX_LOADING = 3;
    var AJAX_DONE = 4;

    var cache = {};

    function get(url, data) {
        return request('GET', url, data);
    }

    function post(url, data) {
        return request('POST', url, data);
    }

    function put(url, data) {
        return request('PUT', url, data);
    }

    function _delete(url, data) {
        return request('DELETE', url, data);
    }

    function getCacheKey(method, url, data) {
        return JSON.stringify({method: method, url: url, data: data});
    }

    function clearCache() {
        cache = {};
    }

    function request(method, url, data) {
        if (method === 'GET') {
            return requestWithCache(method, url, data);
        }
        clearCache();
        return requestWithAjax(method, url, data);
    }

    function requestWithCache(method, url, data) {
        var cacheKey = getCacheKey(method, url, data);
        if (_.has(cache, cacheKey)) {
            return promise.make(function(resolve, reject) {
                resolve(cache[cacheKey]);
            });
        }

        return promise.make(function(resolve, reject) {
            promise.wait(requestWithAjax(method, url, data))
                .then(function(response) {
                    setCache(method, url, data, response);
                    resolve(response);
                }).fail(function(response) {
                    reject(response);
                });
        });
    }

    function setCache(method, url, data, response) {
        var cacheKey = getCacheKey(method, url, data);
        cache[cacheKey] = response;
    }

    function requestWithAjax(method, url, data) {
        var fullUrl = baseUrl + '/' + url;
        fullUrl = fullUrl.replace(/\/{2,}/, '/');

        var xhr = null;
        var apiPromise = promise.make(function(resolve, reject) {
            var options = {
                headers: {
                    'X-Authorization-Token': appState.get('loginToken') || '',
                },
                success: function(data, textStatus, xhr) {
                    resolve({
                        status: xhr.status,
                        json: stripMeta(data)});
                },
                error: function(xhr, textStatus, errorThrown) {
                    reject({
                        status: xhr.status,
                        json: xhr.responseJSON ?
                            stripMeta(xhr.responseJSON) :
                            {error: errorThrown}});
                },
                type: method,
                url: fullUrl,
                data: data,
                cache: false,
            };
            if (data instanceof FormData) {
                options.processData = false;
                options.contentType = false;
            }
            xhr = jQuery.ajax(options);
        });
        apiPromise.xhr = xhr;
        return apiPromise;
    }

    function stripMeta(data) {
        var result = {};
        _.each(data, function(v, k) {
            if (!k.match(/^__/)) {
                result[k] = v;
            }
        });
        return result;
    }

    return {
        get: get,
        post: post,
        put: put,
        delete: _delete,

        AJAX_UNSENT: AJAX_UNSENT,
        AJAX_OPENED: AJAX_OPENED,
        AJAX_HEADERS_RECEIVED: AJAX_HEADERS_RECEIVED,
        AJAX_LOADING: AJAX_LOADING,
        AJAX_DONE: AJAX_DONE,
    };

};

App.DI.registerSingleton('api', ['_', 'jQuery', 'promise', 'appState'], App.API);