// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import AppDispatcher from 'dispatcher/app_dispatcher.jsx';

import PreferenceStore from 'stores/preference_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';

import {getChannelMembersForUserIds} from 'actions/channel_actions.jsx';
import {loadStatusesForProfilesList, loadStatusesForProfilesMap} from 'actions/status_actions.jsx';

import {getDirectChannelName, getUserIdFromChannelName} from 'utils/utils.jsx';

import * as AsyncClient from 'utils/async_client.jsx';
import Client from 'client/web_client.jsx';

import {Constants, ActionTypes, Preferences} from 'utils/constants.jsx';
import {browserHistory} from 'react-router/es6';

export function switchFromLdapToEmail(email, password, token, ldapPassword, onSuccess, onError) {
    Client.ldapToEmail(
        email,
        password,
        token,
        ldapPassword,
        (data) => {
            if (data.follow_link) {
                window.location.href = data.follow_link;
            }

            if (onSuccess) {
                onSuccess(data);
            }
        },
        onError
    );
}

export function loadProfilesAndTeamMembers(offset, limit, teamId = TeamStore.getCurrentId(), success, error) {
    Client.getProfilesInTeam(
        teamId,
        offset,
        limit,
        (data) => {
            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_PROFILES_IN_TEAM,
                profiles: data,
                team_id: teamId,
                offset,
                count: Object.keys(data).length
            });

            loadTeamMembersForProfilesMap(data, teamId, success, error);
            loadStatusesForProfilesMap(data);
        },
        (err) => {
            AsyncClient.dispatchError(err, 'getProfilesInTeam');
        }
    );
}

export function loadProfilesAndTeamMembersAndChannelMembers(offset, limit, teamId = TeamStore.getCurrentId(), channelId = ChannelStore.getCurrentId(), success, error) {
    Client.getProfilesInChannel(
        channelId,
        offset,
        limit,
        (data) => {
            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_PROFILES_IN_CHANNEL,
                profiles: data,
                channel_id: channelId,
                offset,
                count: Object.keys(data).length
            });

            loadTeamMembersForProfilesMap(
                data,
                teamId,
                () => {
                    loadChannelMembersForProfilesMap(data, channelId, success, error);
                    loadStatusesForProfilesMap(data);
                });
        },
        (err) => {
            AsyncClient.dispatchError(err, 'getProfilesInChannel');
        }
    );
}

export function loadTeamMembersForProfilesMap(profiles, teamId = TeamStore.getCurrentId(), success, error) {
    const membersToLoad = {};
    for (const pid in profiles) {
        if (!profiles.hasOwnProperty(pid)) {
            continue;
        }

        if (!TeamStore.hasActiveMemberInTeam(teamId, pid)) {
            membersToLoad[pid] = true;
        }
    }

    const list = Object.keys(membersToLoad);
    if (list.length === 0) {
        if (success) {
            success({});
        }
        return;
    }

    loadTeamMembersForProfiles(list, teamId, success, error);
}

export function loadTeamMembersForProfilesList(profiles, teamId = TeamStore.getCurrentId(), success, error) {
    const membersToLoad = {};
    for (let i = 0; i < profiles.length; i++) {
        const pid = profiles[i].id;

        if (!TeamStore.hasActiveMemberInTeam(teamId, pid)) {
            membersToLoad[pid] = true;
        }
    }

    const list = Object.keys(membersToLoad);
    if (list.length === 0) {
        if (success) {
            success({});
        }
        return;
    }

    loadTeamMembersForProfiles(list, teamId, success, error);
}

export function loadProfilesWithoutTeam(page, perPage, success, error) {
    Client.getProfilesWithoutTeam(
        page,
        perPage,
        (data) => {
            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_PROFILES_WITHOUT_TEAM,
                profiles: data,
                page
            });

            loadStatusesForProfilesMap(data);
        },
        (err) => {
            AsyncClient.dispatchError(err, 'getProfilesWithoutTeam');

            if (error) {
                error(err);
            }
        }
    );
}

function loadTeamMembersForProfiles(userIds, teamId, success, error) {
    Client.getTeamMembersByIds(
        teamId,
        userIds,
        (data) => {
            const memberMap = {};
            for (let i = 0; i < data.length; i++) {
                memberMap[data[i].user_id] = data[i];
            }

            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_MEMBERS_IN_TEAM,
                team_id: teamId,
                team_members: memberMap
            });

            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'getTeamMembersByIds');

            if (error) {
                error(err);
            }
        }
    );
}

export function loadChannelMembersForProfilesMap(profiles, channelId = ChannelStore.getCurrentId(), success, error) {
    const membersToLoad = {};
    for (const pid in profiles) {
        if (!profiles.hasOwnProperty(pid)) {
            continue;
        }

        if (!ChannelStore.hasActiveMemberInChannel(channelId, pid)) {
            membersToLoad[pid] = true;
        }
    }

    const list = Object.keys(membersToLoad);
    if (list.length === 0) {
        if (success) {
            success({});
        }
        return;
    }

    getChannelMembersForUserIds(channelId, list, success, error);
}

export function loadTeamMembersAndChannelMembersForProfilesList(profiles, teamId = TeamStore.getCurrentId(), channelId = ChannelStore.getCurrentId(), success, error) {
    loadTeamMembersForProfilesList(profiles, teamId, () => {
        loadChannelMembersForProfilesList(profiles, channelId, success, error);
    }, error);
}

export function loadChannelMembersForProfilesList(profiles, channelId = ChannelStore.getCurrentId(), success, error) {
    const membersToLoad = {};
    for (let i = 0; i < profiles.length; i++) {
        const pid = profiles[i].id;

        if (!ChannelStore.hasActiveMemberInChannel(channelId, pid)) {
            membersToLoad[pid] = true;
        }
    }

    const list = Object.keys(membersToLoad);
    if (list.length === 0) {
        if (success) {
            success({});
        }
        return;
    }

    getChannelMembersForUserIds(channelId, list, success, error);
}

function populateDMChannelsWithProfiles(userIds) {
    const currentUserId = UserStore.getCurrentId();

    for (let i = 0; i < userIds.length; i++) {
        const channelName = getDirectChannelName(currentUserId, userIds[i]);
        const channel = ChannelStore.getByName(channelName);
        if (channel) {
            UserStore.saveUserIdInChannel(channel.id, userIds[i]);
        }
    }
}

function populateChannelWithProfiles(channelId, userIds) {
    for (let i = 0; i < userIds.length; i++) {
        UserStore.saveUserIdInChannel(channelId, userIds[i]);
    }
    UserStore.emitInChannelChange();
}

export function loadNewDMIfNeeded(channelId) {
    function checkPreference(channel) {
        const userId = getUserIdFromChannelName(channel);

        if (!userId) {
            return;
        }

        const pref = PreferenceStore.getBool(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, userId, false);
        if (pref === false) {
            PreferenceStore.setPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, userId, 'true');
            AsyncClient.savePreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, userId, 'true');
            loadProfilesForDM();
        }
    }

    const channel = ChannelStore.get(channelId);
    if (channel) {
        checkPreference(channel);
    } else {
        Client.getChannel(
            channelId,
            (data) => {
                AppDispatcher.handleServerAction({
                    type: ActionTypes.RECEIVED_CHANNEL,
                    channel: data.channel,
                    member: data.member
                });

                checkPreference(data.channel);
            },
            (err) => {
                AsyncClient.dispatchError(err, 'getChannel');
            }
       );
    }
}

export function loadNewGMIfNeeded(channelId) {
    function checkPreference() {
        const pref = PreferenceStore.getBool(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channelId, false);
        if (pref === false) {
            PreferenceStore.setPreference(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channelId, 'true');
            AsyncClient.savePreference(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channelId, 'true');
            loadProfilesForGM();
        }
    }

    const channel = ChannelStore.get(channelId);
    if (channel) {
        checkPreference();
    } else {
        Client.getChannel(
            channelId,
            (data) => {
                AppDispatcher.handleServerAction({
                    type: ActionTypes.RECEIVED_CHANNEL,
                    channel: data.channel,
                    member: data.member
                });

                checkPreference();
            },
            (err) => {
                AsyncClient.dispatchError(err, 'getChannel');
            }
       );
    }
}

export function loadProfilesForSidebar() {
    loadProfilesForDM();
    loadProfilesForGM();
}

export function loadProfilesForGM() {
    const channels = ChannelStore.getChannels();
    const newPreferences = [];

    for (let i = 0; i < channels.length; i++) {
        const channel = channels[i];
        if (channel.type !== Constants.GM_CHANNEL) {
            continue;
        }

        if (UserStore.getProfileListInChannel(channel.id).length >= Constants.MIN_USERS_IN_GM) {
            continue;
        }

        const isVisible = PreferenceStore.getBool(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channel.id);

        if (!isVisible) {
            const member = ChannelStore.getMyMember(channel.id);
            if (!member || (member.mention_count === 0 && member.msg_count >= channel.total_msg_count)) {
                continue;
            }

            newPreferences.push({
                user_id: UserStore.getCurrentId(),
                category: Preferences.CATEGORY_GROUP_CHANNEL_SHOW,
                name: channel.id,
                value: 'true'
            });
        }

        Client.getProfilesInChannel(
            channel.id,
            0,
            Constants.MAX_USERS_IN_GM,
            (data) => {
                AppDispatcher.handleServerAction({
                    type: ActionTypes.RECEIVED_PROFILES,
                    profiles: data
                });

                populateChannelWithProfiles(channel.id, Object.keys(data));
            }
        );
    }

    if (newPreferences.length > 0) {
        AsyncClient.savePreferences(newPreferences);
    }
}

export function loadProfilesForDM() {
    const channels = ChannelStore.getChannels();
    const newPreferences = [];
    const profilesToLoad = [];
    const profileIds = [];

    for (let i = 0; i < channels.length; i++) {
        const channel = channels[i];
        if (channel.type !== Constants.DM_CHANNEL) {
            continue;
        }

        const teammateId = channel.name.replace(UserStore.getCurrentId(), '').replace('__', '');
        const isVisible = PreferenceStore.getBool(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammateId);

        if (!isVisible) {
            const member = ChannelStore.getMyMember(channel.id);
            if (!member || member.mention_count === 0) {
                continue;
            }

            newPreferences.push({
                user_id: UserStore.getCurrentId(),
                category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
                name: teammateId,
                value: 'true'
            });
        }

        if (!UserStore.hasProfile(teammateId)) {
            profilesToLoad.push(teammateId);
        }
        profileIds.push(teammateId);
    }

    if (newPreferences.length > 0) {
        AsyncClient.savePreferences(newPreferences);
    }

    if (profilesToLoad.length > 0) {
        Client.getProfilesByIds(
            profilesToLoad,
            (data) => {
                AppDispatcher.handleServerAction({
                    type: ActionTypes.RECEIVED_PROFILES,
                    profiles: data
                });

                // Use membersToLoad so we get all the DM profiles even if they were already loaded
                populateDMChannelsWithProfiles(profileIds);
            },
            (err) => {
                AsyncClient.dispatchError(err, 'getProfilesByIds');
            }
        );
    } else {
        populateDMChannelsWithProfiles(profileIds);
    }
}

export function saveTheme(teamId, theme, onSuccess, onError) {
    AsyncClient.savePreference(
        Preferences.CATEGORY_THEME,
        teamId,
        JSON.stringify(theme),
        () => {
            onThemeSaved(teamId, theme, onSuccess);
        },
        (err) => {
            onError(err);
        }
    );
}

function onThemeSaved(teamId, theme, onSuccess) {
    const themePreferences = PreferenceStore.getCategory(Preferences.CATEGORY_THEME);

    if (teamId !== '' && themePreferences.size > 1) {
        // no extra handling to be done to delete team-specific themes
        onSuccess();
        return;
    }

    const toDelete = [];

    for (const [name] of themePreferences) {
        if (name === '' || name === teamId) {
            continue;
        }

        toDelete.push({
            user_id: UserStore.getCurrentId(),
            category: Preferences.CATEGORY_THEME,
            name
        });
    }

    if (toDelete.length > 0) {
        // we're saving a new global theme so delete any team-specific ones
        AsyncClient.deletePreferences(toDelete);

        // delete them locally before we hear from the server so that the UI flow is smoother
        AppDispatcher.handleServerAction({
            type: ActionTypes.DELETED_PREFERENCES,
            preferences: toDelete
        });
    }

    onSuccess();
}

export function searchUsers(term, teamId = TeamStore.getCurrentId(), options = {}, success, error) {
    Client.searchUsers(
        term,
        teamId,
        options,
        (data) => {
            loadStatusesForProfilesList(data);

            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'searchUsers');

            if (error) {
                error(err);
            }
        }
    );
}

export function searchUsersNotInTeam(term, teamId = TeamStore.getCurrentId(), options = {}, success, error) {
    Client.searchUsersNotInTeam(
        term,
        teamId,
        options,
        (data) => {
            loadStatusesForProfilesList(data);

            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'searchUsersNotInTeam');

            if (error) {
                error(err);
            }
        }
    );
}

export function autocompleteUsersInChannel(username, channelId, success, error) {
    Client.autocompleteUsersInChannel(
        username,
        channelId,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'autocompleteUsersInChannel');

            if (error) {
                error(err);
            }
        }
    );
}

export function autocompleteUsersInTeam(username, success, error) {
    Client.autocompleteUsersInTeam(
        username,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'autocompleteUsersInTeam');

            if (error) {
                error(err);
            }
        }
    );
}

export function autocompleteUsers(username, success, error) {
    Client.autocompleteUsers(
        username,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'autocompleteUsers');

            if (error) {
                error(err);
            }
        }
    );
}

export function updateUser(username, type, success, error) {
    Client.updateUser(
        username,
        type,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            if (error) {
                error(err);
            } else {
                AsyncClient.dispatchError(err, 'updateUser');
            }
        }
    );
}

export function generateMfaSecret(success, error) {
    Client.generateMfaSecret(
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'generateMfaSecret');

            if (error) {
                error(err);
            }
        }
    );
}

export function updateUserNotifyProps(data, success, error) {
    Client.updateUserNotifyProps(
      data,
      () => {
          AsyncClient.getMe();

          if (success) {
              success();
          }
      },
      (err) => {
          if (error) {
              error(err);
          }
      }
    );
}

export function updateUserRoles(userId, newRoles, success, error) {
    Client.updateUserRoles(
        userId,
        newRoles,
        () => {
            AsyncClient.getUser(
                userId,
                success,
                error
            );
        },
        error
    );
}

export function activateMfa(code, success, error) {
    Client.updateMfa(
        code,
        true,
        () => {
            AsyncClient.getMe();

            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function deactivateMfa(success, error) {
    Client.updateMfa(
        '',
        false,
        () => {
            AsyncClient.getMe();

            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function checkMfa(loginId, success, error) {
    if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') {
        success(false);
        return;
    }

    Client.checkMfa(
        loginId,
        (data) => {
            if (success) {
                success(data && data.mfa_required === 'true');
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function updateActive(userId, active, success, error) {
    Client.updateActive(userId, active,
        (data) => {
            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_PROFILE,
                profile: data
            });

            if (success) {
                success(data);
            }
        },
        error
    );
}

export function updatePassword(userId, currentPassword, newPassword, success, error) {
    Client.updatePassword(userId, currentPassword, newPassword,
        () => {
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function verifyEmail(uid, hid, success, error) {
    Client.verifyEmail(
        uid,
        hid,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function resetPassword(code, password, success, error) {
    Client.resetPassword(
        code,
        password,
        () => {
            browserHistory.push('/login?extra=' + ActionTypes.PASSWORD_CHANGE);

            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function resendVerification(email, success, error) {
    Client.resendVerification(
        email,
        () => {
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function loginById(userId, password, mfaToken, success, error) {
    Client.loginById(
        userId,
        password,
        mfaToken,
        () => {
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function createUserWithInvite(user, data, emailHash, inviteId, success, error) {
    Client.createUserWithInvite(
        user,
        data,
        emailHash,
        inviteId,
        (response) => {
            if (success) {
                success(response);
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function webLogin(loginId, password, token, success, error) {
    Client.webLogin(
        loginId,
        password,
        token,
        () => {
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function webLoginByLdap(loginId, password, token, success, error) {
    Client.webLoginByLdap(
        loginId,
        password,
        token,
        (data) => {
            if (success) {
                success(data);
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function getAuthorizedApps(success, error) {
    Client.getAuthorizedApps(
        (authorizedApps) => {
            if (success) {
                success(authorizedApps);
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        });
}

export function deauthorizeOAuthApp(appId, success, error) {
    Client.deauthorizeOAuthApp(
        appId,
        () => {
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        });
}

export function uploadProfileImage(userPicture, success, error) {
    Client.uploadProfileImage(
        userPicture,
        () => {
            AsyncClient.getMe();
            if (success) {
                success();
            }
        },
        (err) => {
            if (error) {
                error(err);
            }
        }
    );
}

export function loadProfiles(offset = UserStore.getPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE, success, error) {
    Client.getProfiles(
        offset,
        limit,
        (data) => {
            AppDispatcher.handleServerAction({
                type: ActionTypes.RECEIVED_PROFILES,
                profiles: data
            });

            if (success) {
                success(data);
            }
        },
        (err) => {
            AsyncClient.dispatchError(err, 'getProfiles');

            if (error) {
                error(err);
            }
        }
    );
}

export function getMissingProfiles(ids, success, error) {
    const missingIds = ids.filter((id) => !UserStore.hasProfile(id));

    if (missingIds.length === 0) {
        return;
    }

    AsyncClient.getProfilesByIds(missingIds, success, error);
}
