// jshint esversion: 6
import {Store} from './Store';

type TestStruct = {
    id: string;
    first: string;
    last: string;
    age: number;
    email: string;
    phone: string;
};

// helpers
const checkAsync = (done, fn) => {
    try {
        fn();
        done();
    } catch (e) {
        done();
    }
};

const checkUsers = (list, ...users) => {
    list = list.map(item => item.last).sort();

    expect(list).toEqual(users.sort());
};

const checkUsersAsync = (done, list, ...users) => {
    checkAsync(done, () => {
        checkUsers(list, ...users);
    });
};

class UsersStore extends Store<TestStruct> {}

const getUsersStore = (props = {}) => {
    const predefinedData: Array<TestStruct> = [
        {
            id: 'user1',
            first: 'James',
            last: 'Alpha',
            age: 54,
            email: 'silence@yandex.ru',
            phone: '(800) 478-5822'
        },
        {
            id: 'user2',
            first: 'Stephan',
            last: 'Bravo',
            age: 37,
            email: 'bear@gmail.com',
            phone: '1-866-800-2102'
        },
        {
            id: 'user3',
            first: 'David',
            last: 'Charlie',
            age: 14,
            email: 'drdavid@mail.ru',
            phone: '18004002102'
        },
        {
            id: 'user4',
            first: 'Mike',
            last: 'Delta',
            age: 29,
            email: 'fish@gmail.com',
            phone: '1 (800) 544-2102'
        }
    ];

    const fields = [
        {id: 'id', type: 'string', isPrimaryKey: true},
        {id: 'first', type: 'string'},
        {id: 'last', type: 'string'},
        {
            id: 'age',
            type: 'number',
            comparator(a, b) {
                return b - a;
            }
        },
        {
            id: 'email',
            type: 'string',
            fieldFilterHandler(value, cb) {
                return cb(/@(.+)\./.exec(value)[1]);
            }
        },
        {
            id: 'phone',
            type: 'string',
            textFilterHandler(value, cb) {
                return cb(value.replace(/[-)(\s]/g, ''));
            }
        },
        {
            id: 'underage',
            type: 'boolean',
            getter(item) {
                return item.age < 18;
            }
        }
    ];

    const store = new UsersStore({...props, fields, key: 'UserStore'});
    store.setItems(predefinedData);
    return store;
};

describe('Store', () => {
    it('custom getter', () => {
        const store = getUsersStore();
        const underageUsers = store.getData().filter(item => item.underage);

        checkUsers(underageUsers, 'Charlie');
    });

    it('paging', done => {
        const store = getUsersStore();
        store.setInitialSorting('last', 'ASC');
        store.setPageSize(2);
        store.onChange(data => checkUsersAsync(done, data, 'Charlie', 'Delta'));
        store.setCurrentPage(2);
    });

    describe('sorting', () => {
        it('by ASC', done => {
            const store = getUsersStore();
            store.onChange(data => checkAsync(done, () => expect(data).to.be.ascendingBy('first')));
            store.sortBy('first', 'ASC');
        });

        it('by DESC', done => {
            const store = getUsersStore();
            store.onChange(data =>
                checkAsync(done, () => expect(data).to.be.descendingBy('first'))
            );
            store.sortBy('first', 'DESC');
        });

        it('ascending order by default', done => {
            const store = getUsersStore();
            store.onChange(data => checkAsync(done, () => expect(data).to.be.ascendingBy('first')));
            store.sortBy('first');
        });

        it('with custom comparator', done => {
            const store = getUsersStore();
            store.onChange(data => checkAsync(done, () => expect(data).to.be.ascendingBy('age')));
            store.sortBy('age', 'DESC');
        });
    });

    describe('filtering', () => {
        it('by text', done => {
            const store = getUsersStore();
            store.onChange(data => checkUsersAsync(done, data, 'Alpha', 'Bravo'));
            store.filterByText('pha');
        });

        it('by text with custom text filter handler', done => {
            const store = getUsersStore();
            store.onChange(data => checkUsersAsync(done, data, 'Alpha', 'Charlie'));
            store.filterByText('8004');
        });

        it('by field', done => {
            const store = getUsersStore();
            store.onChange(data => checkUsersAsync(done, data, 'Alpha'));
            store.filterByField('first', 'James');
        });

        it('by field with custom field filter handler', done => {
            const store = getUsersStore();
            store.onChange(data => checkUsersAsync(done, data, 'Bravo', 'Delta'));
            store.filterByField('email', 'gmail');
        });

        it("by field with action ('in')", done => {
            const store = getUsersStore();
            store.onChange(data => checkUsersAsync(done, data, 'Alpha', 'Bravo'));
            store.filterByField('last', 'in', ['Alpha', 'Echo', 'Bravo']);
        });
    });

    describe('cloning', () => {
        it('should keep inheritance chain', () => {
            const store = getUsersStore();
            const clonedStore = store.clone();
            expect(clonedStore instanceof UsersStore).toBeTruthy();
        });

        it('should provide a new store with the same data as source', done => {
            const store = new Store({
                fields: {
                    id: {
                        type: Number,
                        primaryKey: true
                    },
                    name: String
                }
            });
            store.setItems([{id: 1, name: 'Beta'}, {id: 2, name: 'Gamma'}, {id: 3, name: 'Alpha'}]);
            store.filterByField('name', 'in', ['Alpha', 'Gamma']);
            store.sortBy('name', 'ASC');

            // TODO UIA-59320 Unable to use sinon.useFakeTimers to test store
            setTimeout(() => {
                const clonedStore = store.clone(); // clone after filtering/sorting applied
                const data = clonedStore.getData();
                expect(data.length).toBe(2);
                expect(data[0].name).toBe('Alpha');
                expect(data[1].name).toBe('Gamma');
                done();
            }, 1000);
        });

        it('should not affects the listeners of cloned store', done => {
            const store = new Store({
                fields: {
                    id: {
                        type: Number,
                        primaryKey: true
                    },
                    name: String
                }
            });
            store.setItems([{id: 1, name: 'Beta'}, {id: 2, name: 'Gamma'}, {id: 3, name: 'Alpha'}]);
            const callbackForSource = jest.fn();
            const callbackForCloned = jest.fn();

            store.onChange(callbackForSource);

            const clonedStore = store.clone();
            clonedStore.onChange(callbackForCloned);
            clonedStore.sortBy('name', 'ASC');

            // TODO UIA-59320 Unable to use sinon.useFakeTimers to test store
            setTimeout(() => {
                expect(callbackForSource).not.toHaveBeenCalled();
                expect(callbackForCloned).toHaveBeenCalled();
                done();
            }, 1000);
        });
    });
});
