define([
    '/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
    var MAX_LAG_BEFORE_TIMEOUT = 30000;
    var Nacl = window.nacl;

    var uid = function () {
        return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
            .toString(32).replace(/\./g, '');
    };

    var signMsg = function (data, signKey) {
        var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
        return Nacl.util.encodeBase64(Nacl.sign.detached(buffer, signKey));
    };

/*
types of messages:
    pin -> hash
    unpin -> hash
    getHash -> hash
    getTotalSize -> bytes
    getFileSize -> bytes
*/

    var sendMsg = function (ctx, data, cb) {
        var network = ctx.network;
        var hkn = network.historyKeeper;
        var txid = uid();

        ctx.pending[txid] = cb;
        return network.sendto(hkn, JSON.stringify([txid, data]));
    };

    var parse = function (msg) {
        try {
            return JSON.parse(msg);
        } catch (e) {
            return null;
        }
    };

    var onMsg = function (ctx, msg) {
        var parsed = parse(msg);

        if (!parsed) {
            return void console.error(new Error('could not parse message: %s', msg));
        }

        var txid = parsed[0];
        var pending = ctx.pending[txid];

        if (!(parsed && parsed.slice)) {
            return void console.error('MALFORMED_RPC_RESPONSE');
        }

        var response = parsed.slice(1);

        if (typeof(pending) === 'function') {
            if (response[0] === 'ERROR') {
                return void pending(response[1]);
            }
            pending(void 0, response);
        } else {
            console.log("No callback provided");
        }
    };

    var create = function (network, edPrivateKey, edPublicKey) {
        var signKey;

        try {
            signKey = Nacl.util.decodeBase64(edPrivateKey);
            if (signKey.length !== 64) {
                throw new Error('private key did not match expected length of 64');
            }
        } catch (err) { throw err; }

        var pubBuffer;
        try {
            pubBuffer = Nacl.util.decodeBase64(edPublicKey);
            if (pubBuffer.length !== 32) {
                throw new Error('expected public key to be 32 uint');
            }
        } catch (err) { throw err; }

        var ctx = {
            seq: new Date().getTime(),
            network: network,
            timeouts: {}, // timeouts
            pending: {}, // callbacks
            cookie: null,
        };

        var send = function (type, msg, cb) {
            // construct a signed message...
            var data = [type, msg];
            var sig = signMsg(data, signKey);

            data.unshift(ctx.cookie); //
            data.unshift(edPublicKey);
            data.unshift(sig);

            // [sig, edPublicKey, cookie, type, msg]
            return sendMsg(ctx, data, cb);
        };

        var getCookie = function (cb) {
            send('COOKIE', "", function (e, msg) {
                console.log('cookie message', e, msg);
                cb(e, msg);
            });
        };

        network.on('message', function (msg, sender) {
            onMsg(ctx, msg);
        });
        return {
            send: send,
            ready: getCookie,
        };
    };

    return { create: create };
});