import _ from 'lodash';
import { Promise } from 'rsvp';
import Immutable from 'immutable';
import { ActionTypes } from 'constants/Common';

import Url from 'lib/Url';
import getErrors from 'lib/getErrors';
import { i18n } from 'lib/i18n';
import toBlob from 'lib/toBlob';

import userApi from 'api/user';
import sync from 'services/sync';

import UserStore from 'stores/Users';
import ConfigStore from 'stores/Config';
import AppActions from 'actions/App/OrgStructure';
import dispatcher from 'dispatcher';

export default {
    createUser,
    updateUser,
    updateUsers,
    addAlias,
    removeAlias,
    blockUser,
    unblockUser,
    grantAdminAccess,
    revokeAdminAccess,
    grantDeputyAdminAccess,
    revokeDeputyAdminAccess,
    dismissUser,
    setAvatar,
    updateStore,
    hasPaidDiskSpace,
    isDismissed,
    isUsed,
    read,
    getList,
    getAdminList,
    updateLanguage,
};

const ROOT_DEPATMENT_ID = 1;
const MAX_AVATAR_IMAGE_FILE_SIZE = 7 * 1024 * 1024;

function pushError(error) {
    return Promise.resolve().then(() => ({
        errors: Immutable.fromJS({ _common: error }),
    }));
}

/**
 * Создание нового пользователя
 * @method createUser
 * @param  {Object}    data
 * @param  {Boolean}   redirect
 * @returns {Promise}
 */
function createUser(data, redirect) {
    return userApi.create(data).then(response => {
        let errors = null;

        // FIXME: Это костыль чтобы показывать вменяемую ошибку
        // Когда будут добавлены коды ошибок его надо будет выпилить
        if (response && response.status === 409) {
            errors = new Immutable.Map({
                nickname: i18n('user_validation.login_occupied'),
            });
        } else {
            errors = getErrors(response);
        }

        if (!errors.isEmpty()) {
            return { errors };
        }

        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);

        const id = (_.find(response.sources, { type: 'user' }) || {}).id;

        if (id && redirect) {
            Url.open(Url.getUserPath(id));
        }

        sync.refreshDepartments([data.department_id], 'shallow');

        if (data.department_id !== ROOT_DEPATMENT_ID) {
            sync.refreshDepartmentCounters([data.department_id]);
        }

        return response;
    });
}

/**
 * Обновление данных о пользователе
 * @method updateUser
 * @param  {Object}    data
 * @param  {String}    id - user id
 * @returns {Promise}
 */
function updateUser(data, id) {
    const originalUser = UserStore.get(id);
    const originalDepartment = originalUser ? originalUser.get('department') : null;

    return userApi.update(data, id).then(response => {
        const errors = getErrors(response);

        if (!errors.isEmpty()) {
            return { errors };
        }

        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);

        // если у пользователя изменился отдел,
        // обновляем его положение в структуре
        const user = UserStore.get(id);
        const department = user ? user.get('department') : null;

        if (originalDepartment !== department) {
            AppActions.showUser(id);
            sync.refreshDepartmentCounters([originalDepartment, department]);
        }
    });
}

function updateUsers(data) {
    return userApi.updateList(data).then(response => {
        const errors = getErrors(response);

        if (!errors.isEmpty()) {
            return { errors };
        }

        const users = {};

        data.forEach(item => {
            const user = item && item.id ? UserStore.get(item.id) : null;

            if (user) {
                users[item.id] = _.merge(user.toJS(), item);
            }
        });

        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, { users });
    });
}

/**
 * Добавление алиаса пользователя
 * @method addAlias
 * @param  {String}   alias
 * @param  {String}   id - user id
 * @returns {Promise}
 */
function addAlias(alias, id) {
    return userApi.addAlias(alias, id).then(response => {
        const errors = getErrors(response);

        if (!errors.isEmpty()) {
            return { errors };
        }

        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);
    });
}

/**
 * Удаление алиаса пользователя
 * @method removeAlias
 * @param  {String}   alias
 * @param  {String}   id - user id
 * @returns {Promise}
 */
function removeAlias(alias, id) {
    return userApi.removeAlias(alias, id).then(response => {
        const errors = getErrors(response);

        if (!errors.isEmpty()) {
            return { errors };
        }

        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);
    });
}

/**
 * Блокировка пользователя
 * @method blockUser
 * @param  {String}    id
 * @returns {Promise}
 */
function blockUser(id) {
    return updateUser({ is_enabled: false }, id);
}

/**
 * Разблокировка пользователя
 * @method unblockUser
 * @param  {String}    id
 * @returns {Promise}
 */
function unblockUser(id) {
    return updateUser({ is_enabled: true }, id);
}

/**
 * Наделение админскими правами
 * @method grantAdminAccess
 * @param  {String}    id
 * @returns {Promise}
 */
function grantAdminAccess(id) {
    return updateUser({ role: 'admin' }, id);
}

/**
 * Отзыв админских прав
 * @method revokeAdminAccess
 * @param  {String}    id
 * @returns {Promise}
 */
function revokeAdminAccess(id) {
    return updateUser({ role: 'user' }, id);
}

/**
 * Наделение админскими правами
 * @method grantAdminAccess
 * @param  {String}    id
 * @returns {Promise}
 */
function grantDeputyAdminAccess(id) {
    return updateUser({ role: 'deputy_admin' }, id);
}

/**
 * Отзыв админских прав
 * @method revokeAdminAccess
 * @param  {String}    id
 * @returns {Promise}
 */
function revokeDeputyAdminAccess(id) {
    return updateUser({ role: 'user' }, id);
}

/**
 * Удаление/увольнение пользователя
 * @method dismissUser
 * @param  {String}    id
 * @returns {Promise}
 */
function dismissUser(id) {
    const originalUser = UserStore.get(id);
    const originalDepartment = originalUser ? originalUser.get('department') : null;

    return userApi.update({ is_dismissed: true }, id)
        .then(response => {
            const errors = getErrors(response);

            if (!errors.isEmpty()) {
                return { errors };
            }

            dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);
            dispatcher.dispatch(ActionTypes.DISCARD_USER, { id });
            // если пользователь удаляется, переключаемся в родительский отдел
            Url.open(Url.getDepartmentPath(originalDepartment));
        });
}

/**
 * Установка аватара пользователя
 * @method  setAvatar
 * @param   {Object}    data  { 'avatar_file' - base64 string | 'avatar_url' }
 * @param   {String}    id
 * @returns {Promise}
 */
function setAvatar(data, id) {
    if (!data.avatar_file) {
        return pushError(i18n('avatar.error.no_file'));
    }

    const blob = toBlob(data.avatar_file);

    if (blob.size > MAX_AVATAR_IMAGE_FILE_SIZE) {
        return pushError(i18n('avatar.error.file_too_large'));
    }

    const formData = new FormData();

    formData.append('avatar_file', blob);
    data = formData;

    return userApi.setAvatar(data, id)
        .then(response => {
            const errors = getErrors(response);

            if (!errors.isEmpty()) {
                return { errors };
            }

            return userApi.read({ id });
        })
        .then(response => {
            dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);
        });
}

/**
 * Обновление стора
 * @method  updateStore
 * @param   {Object}    userData
 * @param   {String}    id
 */
function updateStore(userData, id) {
    const user = UserStore.get(id);

    if (user) {
        dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, {
            users: {
                [id]: _.extend(user.toJS(), userData),
            },
        });
    }
}

function hasPaidDiskSpace(id) {
    return userApi.read({ id, fields: 'id,services.disk.has_paid_space', v: 3 })
        .then(response => {
            const diskData = _.find(_.get(response, `users.${id}.services`), { slug: 'disk' });

            return _.get(diskData, 'has_paid_space');
        });
}

function isDismissed(nickname) {
    return userApi.getList({ nickname, is_dismissed: true, v: 3 })
        .then(response => !_.isEmpty(_.get(response, 'users')));
}

function isUsed(nickname) {
    return userApi.getList({ nickname, v: 3 })
        .then(response => !_.isEmpty(_.get(response, 'users')));
}

function read(options) {
    return userApi.read(options)
        .then(response => {
            const errors = getErrors(response);

            if (!errors.isEmpty()) {
                return { errors };
            }

            dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);

            return response;
        });
}

function getList(options) {
    return userApi.getList(options)
        .then(response => {
            const errors = getErrors(response);

            if (!errors.isEmpty()) {
                return { errors };
            }

            dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);

            return response;
        });
}

function getAdminList(options) {
    return userApi.getAdminList(options)
        .then(response => {
            const errors = getErrors(response);

            if (!errors.isEmpty()) {
                return { errors };
            }

            dispatcher.dispatch(ActionTypes.RECEIVE_MIXED_DATA, response);

            return response;
        });
}

/**
 * Изменяет текущий язык пользователя
 * @param {String} lang
 */
function updateLanguage(lang) {
    userApi.getSK()
        .then(data => {
            Url.open(ConfigStore.get('app.changeLanguage', {
                intl: lang,
                retpath: encodeURIComponent(location.href),
                sk: data.sk,
            }));
        });
}
