import _ from 'lodash';
import Immutable from 'immutable';
import { ReduceStore } from 'flux/utils';
import { ActionTypes } from 'constants/Common';
import dispatcher from 'dispatcher';
import User from 'records/User';

const cacheKeyStorage = {};

function getRandomKey() {
    return Math.floor(1e6 * Math.random());
}

function compareByName(a, b) {
    const aName = a.toSortableString();
    const bName = b.toSortableString();

    if (aName === bName) {
        return 0;
    }

    return aName > bName ? 1 : -1;
}

class UserStore extends ReduceStore {
    getInitialState() {
        return new Immutable.Map();
    }

    reduce(state, action) {
        const { data, type } = action;

        switch (type) {
            case ActionTypes.RECEIVE_MIXED_DATA:
                return this._receiveRawData(state, data);
            case ActionTypes.UPDATE_USER:
                return this._update(state, data.id);
            case ActionTypes.RECEIVE_USER:
                return state.set(data.id, data);
            case ActionTypes.DISCARD_USER:
                return state.remove(data.id);
            default:
                return state;
        }
    }

    /**
     * Возвращает пользователя по id
     * @method  get
     * @param   {String} id
     * @returns {?User}
     */
    get(id) {
        return this._state.get(id);
    }

    find(props) {
        return this.get(
            _.get(_.filter(this._state.toJS(), _.matches(props))[0], 'id')
        );
    }

    getActiveUsers() {
        const users = this._state.toArray() || [];

        return users.filter(user => !user.get('is_dismissed'));
    }

    getActiveUserList(filter) {
        let list = this.getActiveUsers();

        if (filter) {
            list = list.filter(filter);
        }

        return list.sort(compareByName);
    }

    getName(id) {
        const item = this.get(id);

        return item ? item.getName() : id;
    }

    getUrl(id) {
        const item = this.get(id);

        return item ? item.getUrl() : null;
    }

    getAdmins() {
        return this._state.filter(item => item.isAdmin()).toArray();
    }

    _createUser(item) {
        const existingUser = this.get(item.id);

        if (!_.every([item.id, item.nickname], Boolean) && existingUser) {
            item = _.extend(existingUser.toJS(), item);
        }

        return User.create(item, this._getCacheKey(item.id));
    }

    /**
     * Мержит в стейт новые данные
     * @protected
     * @method  _receiveRawData
     * @param   {Immutable.Map}  state
     * @param   {Object}         data
     * @returns {Immutable.Map}
     */
    _receiveRawData(state, data) {
        if (data.users) {
            const nextState = new Immutable.Map(data.users)
                .map(item => this._createUser(item));

            return state.merge(nextState);
        }

        return state;
    }

    _getCacheKey(id) {
        if (!cacheKeyStorage[id]) {
            cacheKeyStorage[id] = getRandomKey();
        }

        return cacheKeyStorage[id];
    }

    /**
     * Обновляет cache_key пользователя id и оповещает слушателей стора
     * о необходимости перерендерить пользователя
     * @protected
     * @method  _update
     * @param   {Immutable.Map}  state
     * @param   {String}         id
     * @returns {Immutable.Map}
     */
    _update(state, id) {
        const user = this.get(id);

        if (user) {
            // обновляем cache_key для пользователя id;
            // в следующий раз при обновлении стора и вызове User.create()
            // в _receiveRawData() будет использовано последнее значение cache_key
            cacheKeyStorage[id] = getRandomKey();

            const userData = user.toJS();

            userData.cache_key = cacheKeyStorage[id];

            return this._receiveRawData(state, {
                users: {
                    [id]: userData,
                },
            });
        }

        return state;
    }
}

export default window.ya.connect.UserStore = new UserStore(dispatcher);
