const SockChatKeepAlive = function(ctx, sendPing, delay) {
    if(typeof ctx !== 'object')
        throw 'ctx must be a function';
    if(typeof sendPing !== 'function')
        throw 'sendPing must be a function';
    if(typeof delay !== 'number')
        throw 'delay must be a number';

    let timeout;
    let failures = 0;

    delay *= 1000;

    const clear = () => {
        if(timeout !== undefined) {
            clearTimeout(timeout);
            timeout = undefined;
        }
    };

    const schedule = () => {
        clear();
        timeout = setTimeout(run, delay);
    };

    const run = () => {
        clear();

        if(!ctx.isAuthed) {
            schedule();
            return;
        }

        ctx.dispatch('ping:send');
        sendPing()
            .then(info => {
                failures = 0;
                ctx.dispatch('ping:recv', info);
            })
            .catch(error => {
                ++failures;
                if(error === 'timeout')
                    ctx.dispatch('ping:long', { failures: failures });
            })
            .finally(() => { schedule(); });
    };

    return {
        start: run,
        stop: () => {
            clear();
            ctx.pingPromise?.cancel();
        },
    };
};
