import { Updux } from './Updux';
import { action } from './actions';

test('basic state', () => {
    const alpha = new Updux({
        initial: { sub: 1 },
    });

    const beta = new Updux({
        initial: { foo: 1 },
    });

    const dux = new Updux({
        initial: { a: 1, b: 'two' },
        subduxes: { alpha, beta },
    });

    expect(dux.initial).toMatchObject({
        a: 1,
        b: 'two',
        alpha: { sub: 1 },
        beta: { foo: 1 },
    });
});

test('basic actions', () => {
    const alpha = new Updux({
        actions: {
            foo: action('foo'),
        },
    });

    const beta = new Updux({});

    const dux = new Updux({
        subduxes: { alpha, beta },
        actions: {
            bar: action('bar'),
        },
    });

    expect(Object.keys(dux.actions).sort()).toMatchObject(['bar', 'foo']);
});

test('setAction', () => {
    const dux = new Updux({
        actions: {
            bar: action('bar'),
        },
    });
    dux.setAction('foo');

    expect(Object.keys(dux.actions).sort()).toMatchObject(['bar', 'foo']);
});

test('basic selectors', () => {
    const alpha = new Updux({
        initial: { quux: 3 },
        selectors: {
            getQuux: ({ quux }) => quux,
        },
    });

    const dux = new Updux({
        initial: {
            foo: 1,
            bar: 4,
        },
        subduxes: { alpha },
        selectors: {
            getBar: ({ bar }) => bar,
        },
    });
    dux.setSelector('getFoo', ({ foo }) => foo);
    dux.setSelector(
        'getAdd',
        ({ foo }) =>
            (add) =>
                add + foo
    );
    dux.setAction('stuff');

    expect(dux.selectors.getBar({ bar: 3 })).toEqual(3);
    expect(dux.selectors.getFoo({ foo: 3 })).toEqual(3);

    expect(alpha.selectors.getQuux({ quux: 1 })).toEqual(1);
    expect(dux.selectors.getQuux({ alpha: { quux: 1 } })).toEqual(1);

    const store = dux.createStore();

    expect(store.getState.getFoo()).toEqual(1);
    expect(store.getState.getQuux()).toEqual(3);
    expect(store.getState.getAdd(7)).toEqual(8);
});

test('mutations', () => {
    const alpha = new Updux({
        initial: { quux: 3 },
    });
    alpha.setAction('add');
    alpha.setMutation('add', (toAdd) => (state) => ({
        quux: state.quux + toAdd,
    }));

    const dux = new Updux({
        initial: {
            foo: 1,
            bar: 4,
        },
        subduxes: { alpha },
    });
    dux.setAction('subtract');
    dux.setMutation('add', (toAdd) => (state) => ({
        ...state,
        foo: state.foo + toAdd,
    }));
    dux.setMutation('subtract', (toSubtract) => (state) => ({
        ...state,
        bar: state.bar - toSubtract,
    }));

    const store = dux.createStore();

    expect(store.getState()).toMatchObject({
        foo: 1,
        bar: 4,
        alpha: { quux: 3 },
    });

    store.dispatch.add(10);

    expect(store.getState()).toMatchObject({
        foo: 11,
        bar: 4,
        alpha: { quux: 13 },
    });

    store.dispatch.subtract(20);

    expect(store.getState()).toMatchObject({
        foo: 11,
        bar: -16,
        alpha: { quux: 13 },
    });
});

test('middleware', () => {
    const fooEffect = jest.fn(() => true);

    const dux = new Updux({
        effects: {
            foo: () => (next) => (action) => {
                fooEffect();
                next(action);
            },
        },
    });

    const store = dux.createStore();

    expect(fooEffect).not.toBeCalled();

    store.dispatch({ type: 'foo' });

    expect(fooEffect).toBeCalled();
});
