import _ from 'lodash';
import promiseMiddleware from 'bitbucket/internal/bbui/utils/promise-middleware';

const dispatch = sinon.spy();
const next = sinon.stub();

const callMiddlewareWithAction = action => promiseMiddleware({ dispatch })(next)(action);

QUnit.module('utils-promise-middleware', {
    afterEach: function() {
        dispatch.reset();
        next.reset();
    },
});

QUnit.test('it ignores actions without meta', function(assert) {
    callMiddlewareWithAction({ type: 'SIMPLE' });

    sinon.assert.calledOnce(next);
    sinon.assert.notCalled(dispatch);
});

QUnit.test('it ignores actions without promise', function(assert) {
    callMiddlewareWithAction({
        type: 'SIMPLE',
        meta: {},
    });

    sinon.assert.calledOnce(next);
    sinon.assert.notCalled(dispatch);
});

function assertDoubleDispatch(assert) {
    return () => {
        sinon.assert.calledTwice(dispatch);
        const firstAction = dispatch.firstCall.args[0];
        assert.ok(firstAction.meta.isPending);
        const secondAction = dispatch.secondCall.args[0];
        assert.ok(!secondAction.meta.isPending);
        return [firstAction, secondAction];
    };
}

QUnit.test('it dispatches on the happy path', function(assert) {
    const done = assert.async();
    const result = callMiddlewareWithAction({
        type: 'OPTIMISTIC',
        payload: 'foo',
        meta: {
            promise: Promise.resolve('resolved'),
        },
    });

    result
        .then(assertDoubleDispatch(assert))
        .then(([firstAction, secondAction]) => {
            assert.ok(_.isNumber(firstAction.meta.actionId));
            assert.equal(firstAction.meta.actionId, secondAction.meta.actionId);
            assert.equal(secondAction.payload, 'resolved');
            done();
        });
});

QUnit.test('it dispatches on the un-happy path', function(assert) {
    const done = assert.async();
    const result = callMiddlewareWithAction({
        type: 'OPTIMISTIC',
        payload: 'foo',
        meta: {
            promise: Promise.reject('rejected'),
        },
    });

    result
        .then(assertDoubleDispatch(assert))
        .then(([firstAction, secondAction]) => { // eslint-disable-line no-unused-vars
            assert.equal(secondAction.payload.error, 'rejected');
            done();
        });
});
