"use strict";

var request = require('request');
var Service = require('./../service');
var md5     = require('./../md5').md5;

var ArrayProto    = Array.prototype;
var nativeKeys    = Object.keys;
var nativeForEach = ArrayProto.forEach;
var slice         = ArrayProto.slice;
var breaker       = {};

var requestOptions = {
    request              : null,
    applicationSecretKey : null,
    applicationId        : null,
    sessionKey           : null,
    refreshToken         : null,
    restBase             : 'http://www.appsmail.ru/platform/api',
    refreshBase          : 'https://appsmail.ru/oauth/token'
};

function keysUnderscore(obj) {
    var keys = [];

    if (!Service.isObject(obj)) return [];
    if (nativeKeys) return nativeKeys(obj);

    for (var key in obj) {
        if (obj.hasOwnProperty(key)) keys.push(key);
    }

    return keys;
}

function encode(str) {
    try {
        return encodeURIComponent(str);
    } catch (e) {
        return str;
    }
}

function stringifyParams(obj){
    if (!obj) return '';
    var pairs = [];

    for (var key in obj) {
        var value = obj[key];

        if (Service.isArray(value)) {
            for (var i = 0; i < value.length; ++i) {
                pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
            }

            continue;
        }

        pairs.push(encode(key) + '=' + encode(obj[key]));
    }

    return pairs.join('&');
}

function _responseHandler(error, response, body, callback) {
    if (error == null && body.hasOwnProperty('error')) {
        error = body.error;
    }

    return callback(error, body, response);
}

function pairs(obj) {
    var _keys  = keysUnderscore(obj);
    var length = _keys.length;
    var pairs  = new Array(length);

    for (var i = 0; i < length; i++) {
        pairs[i] = [_keys[i], obj[_keys[i]]];
    }

    return pairs;
}

var each = function(obj, iterator, context) {
    if (obj == null) return obj;

    if (nativeForEach && obj.forEach === nativeForEach) {
        obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
        for (var i = 0, length = obj.length; i < length; i++) {
            if (iterator.call(context, obj[i], i, obj) === breaker) return;
        }
    } else {
        var _keys = keysUnderscore(obj);

        for (var i = 0, length = _keys.length; i < length; i++) {
            if (iterator.call(context, obj[_keys[i]], _keys[i], obj) === breaker) return;
        }
    }

    return obj;
};

function first(array, n, guard) {
    if (array == null) return void 0;
    if ((n == null) || guard) return array[0];
    if (n < 0) return [];

    return slice.call(array, 0, n);
}

function last(array, n, guard) {
    if (array == null) return void 0;
    if ((n == null) || guard) return array[array.length - 1];

    return slice.call(array, Math.max(array.length - n, 0));
}

//==========================================================

var MailRuApi = (function() {
    var makeRequest, makeSignature, parametrize, validateOptions;

    function _mailRuApi(method, postData, callback) {
        validateOptions();
        makeRequest(method, postData, callback);
    }

    makeRequest = function(method, postData, callback) {
        var getUrl, requestedData;
        requestedData = {
            app_id: requestOptions['applicationId'],
            session_key: requestOptions['sessionKey'],
            sig: makeSignature(postData)
        };
        Service.extend(requestedData, postData);
        switch (method.toUpperCase()) {
            case 'POST':
                return request.post({
                    uri: requestOptions['restBase'],
                    json: true,
                    headers: {
                        'content-type': 'application/x-www-form-urlencoded'
                    },
                    body: stringifyParams(requestedData)
                }, function(error, response, body) {
                    return _responseHandler(error, response, body, callback);
                });
            case 'GET':
                getUrl = ("" + requestOptions['restBase'] + "?") + parametrize(requestedData, true);

                return request.get({
                    uri: getUrl,
                    json: true
                }, function(error, response, body) {
                    return _responseHandler(error, response, body, callback);
                });
            default:
                throw 'HTTP verb not supported';
        }
    };

    makeSignature = function(postData) {
        var secret, sortedParams;

        postData['app_id']      = requestOptions['applicationId'];
        postData['session_key'] = requestOptions['sessionKey'];
        postData['secure']      = 1;

        sortedParams = parametrize(postData);
        secret = requestOptions['applicationSecretKey'];

        return md5(sortedParams + secret);
    };

    parametrize = function(obj, join) {
        var arrayOfArrays, sortedParams, symbol;

        arrayOfArrays = pairs(obj).sort();
        symbol        = (join) ? '&' : '';
        sortedParams  = '';

        each(arrayOfArrays, function(value) {
            return sortedParams += ("" + (first(value)) + "=" + (last(value))) + symbol;
        });

        return sortedParams;
    };

    validateOptions = function() {
        if (!((requestOptions['applicationId'] != null) || (requestOptions['applicationSecretKey'] != null))) {
            throw 'Please setup requestOptions with valid params';
        }

        if (requestOptions['sessionKey'] == null) {
            throw 'sessionKey does not initialized';
        }
    };

    return _mailRuApi;

})();

exports.api = MailRuApi;

exports.refresh = function(refreshToken, callback) {
    var refresh_params;
    requestOptions['refreshToken'] = refreshToken;
    refresh_params = {
        refresh_token: requestOptions['refreshToken'],
        grant_type: 'refresh_token',
        client_id: requestOptions['applicationId'],
        client_secret: requestOptions['applicationSecretKey']
    };
    return request.post({
        uri: requestOptions['refreshBase'],
        json: true,
        headers: {
            'content-type': 'application/x-www-form-urlencoded'
        },
        body: stringifyParams(refresh_params)
    }, function(error, response, body) {
        return _responseHandler(error, response, body, callback);
    });
};

exports.post = function(params, callback) {
    return new MailRuApi('POST', params, callback);
};

exports.get = function(params, callback) {
    return new MailRuApi('GET', params, callback);
};

exports.setSessionKey = function(token) {
    return Service.extend(requestOptions, {
        sessionKey: token
    });
};

exports.getSessionKey = function() {
    return requestOptions['sessionKey'];
};

exports.setOptions = function(options) {
    if (typeof options === 'object') {
        request = options.request || request;

        return Service.extend(requestOptions, options);
    }
};

exports.getOptions = function() {
    return requestOptions;
};
