var App = App || {};

App.Auth = function(_, jQuery, util, api, appState, promise) {

    var privileges = {
        register: 'register',
        listUsers: 'listUsers',
        viewUsers: 'viewUsers',
        viewAllAccessRanks: 'viewAllAccessRanks',
        viewAllEmailAddresses: 'viewAllEmailAddresses',
        changeAccessRank: 'changeAccessRank',
        changeOwnAvatarStyle: 'changeOwnAvatarStyle',
        changeOwnEmailAddress: 'changeOwnEmailAddress',
        changeOwnName: 'changeOwnName',
        changeOwnPassword: 'changeOwnPassword',
        changeAllAvatarStyles: 'changeAllAvatarStyles',
        changeAllEmailAddresses: 'changeAllEmailAddresses',
        changeAllNames: 'changeAllNames',
        changeAllPasswords: 'changeAllPasswords',
        deleteOwnAccount: 'deleteOwnAccount',
        deleteAllAccounts: 'deleteAllAccounts',
        banUsers: 'banUsers',

        listPosts: 'listPosts',
        viewPosts: 'viewPosts',
        uploadPosts: 'uploadPosts',
        uploadPostsAnonymously: 'uploadPostsAnonymously',
        deletePosts: 'deletePosts',
        featurePosts: 'featurePosts',
        changePostSafety: 'changePostSafety',
        changePostSource: 'changePostSource',
        changePostTags: 'changePostTags',
        changePostContent: 'changePostContent',
        changePostThumbnail: 'changePostThumbnail',
        changePostRelations: 'changePostRelations',
        changePostFlags: 'changePostFlags',

        addPostNotes: 'addPostNotes',
        editPostNotes: 'editPostNotes',
        deletePostNotes: 'deletePostNotes',

        listComments: 'listComments',
        addComments: 'addComments',
        editOwnComments: 'editOwnComments',
        editAllComments: 'editAllComments',
        deleteOwnComments: 'deleteOwnComments',
        deleteAllComments: 'deleteAllComments',
        deleteTags: 'deleteTags',
        mergeTags: 'mergeTags',

        listTags: 'listTags',
        massTag: 'massTag',
        changeTagName: 'changeTagName',
        changeTagCategory: 'changeTagCategory',
        changeTagImplications: 'changeTagImplications',
        changeTagSuggestions: 'changeTagSuggestions',
        banTags: 'banTags',

        viewHistory: 'viewHistory',
    };

    function loginFromCredentials(userNameOrEmail, password, remember) {
        return promise.make(function(resolve, reject) {
            promise.wait(api.post('/login', {userNameOrEmail: userNameOrEmail, password: password}))
                .then(function(response) {
                    updateAppState(response);
                    jQuery.cookie(
                        'auth',
                        response.json.token.name,
                        remember ? { expires: 365 } : {});
                    resolve(response);
                }).fail(function(response) {
                    reject(response);
                });
        });
    }

    function loginFromToken(token, isFromCookie) {
        return promise.make(function(resolve, reject) {
            var fd = {
                token: token,
                isFromCookie: isFromCookie
            };
            promise.wait(api.post('/login', fd))
                .then(function(response) {
                    updateAppState(response);
                    resolve(response);
                }).fail(function(response) {
                    reject(response);
                });
        });
    }

    function loginAnonymous() {
        return promise.make(function(resolve, reject) {
            promise.wait(api.post('/login'))
                .then(function(response) {
                    updateAppState(response);
                    resolve(response);
                }).fail(function(response) {
                    reject(response);
                });
        });
    }

    function logout() {
        return promise.make(function(resolve, reject) {
            jQuery.removeCookie('auth');
            appState.set('loginToken', null);
            return promise.wait(loginAnonymous())
                .then(resolve)
                .fail(reject);
        });
    }

    function tryLoginFromCookie() {
        return promise.make(function(resolve, reject) {
            if (isLoggedIn()) {
                resolve();
                return;
            }

            var authCookie = jQuery.cookie('auth');
            if (!authCookie) {
                reject();
                return;
            }

            promise.wait(loginFromToken(authCookie, true))
                .then(function(response) {
                    resolve();
                }).fail(function(response) {
                    jQuery.removeCookie('auth');
                    reject();
                });
        });
    }

    function updateAppState(response) {
        appState.set('privileges', response.json.privileges || []);
        appState.set('loginToken', response.json.token && response.json.token.name);
        appState.set('loggedIn', response.json.user && !!response.json.user.id);
        appState.set('loggedInUser', response.json.user);
        appState.set('config', response.json.config);
    }

    function isLoggedIn(userName) {
        if (!appState.get('loggedIn')) {
            return false;
        }
        if (typeof(userName) !== 'undefined') {
            if (getCurrentUser().name !== userName) {
                return false;
            }
        }
        return true;
    }

    function getCurrentUser() {
        return appState.get('loggedInUser');
    }

    function getCurrentPrivileges() {
        return appState.get('privileges');
    }

    function updateCurrentUser(user) {
        if (user.id !== getCurrentUser().id) {
            throw new Error('Cannot set current user to other user this way.');
        }
        appState.set('loggedInUser', user);
    }

    function hasPrivilege(privilege) {
        return _.contains(getCurrentPrivileges(), privilege);
    }

    function startObservingLoginChanges(listenerName, callback) {
        appState.startObserving('loggedInUser', listenerName, callback);
    }

    return {
        loginFromCredentials: loginFromCredentials,
        loginFromToken: loginFromToken,
        loginAnonymous: loginAnonymous,
        tryLoginFromCookie: tryLoginFromCookie,
        logout: logout,

        startObservingLoginChanges: startObservingLoginChanges,
        isLoggedIn: isLoggedIn,
        getCurrentUser: getCurrentUser,
        updateCurrentUser: updateCurrentUser,
        getCurrentPrivileges: getCurrentPrivileges,
        hasPrivilege: hasPrivilege,

        privileges: privileges,
    };

};

App.DI.registerSingleton('auth', ['_', 'jQuery', 'util', 'api', 'appState', 'promise'], App.Auth);
