'use strict';

const page = require('page');
const api = require('../api.js');
const config = require('../config.js');
const events = require('../events.js');
const misc = require('../util/misc.js');
const views = require('../util/views.js');
const topNavController = require('../controllers/top_nav_controller.js');
const pageController = require('../controllers/page_controller.js');
const RegistrationView = require('../views/registration_view.js');
const UserView = require('../views/user_view.js');
const UserListHeaderView = require('../views/user_list_header_view.js');
const UserListPageView = require('../views/user_list_page_view.js');
const EmptyView = require('../views/empty_view.js');

const rankNames = {
    anonymous: 'Anonymous',
    restricted: 'Restricted user',
    regular: 'Regular user',
    power: 'Power user',
    moderator: 'Moderator',
    administrator: 'Administrator',
    nobody: 'Nobody',
};

class UsersController {
    constructor() {
        this.registrationView = new RegistrationView();
        this.userView = new UserView();
        this.userListHeaderView = new UserListHeaderView();
        this.userListPageView = new UserListPageView();
        this.emptyView = new EmptyView();
    }

    registerRoutes() {
        page('/register', () => { this.createUserRoute(); });
        page(
            '/users/:query?',
            (ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
            (ctx, next) => { this.listUsersRoute(ctx, next); });
        page(
            '/user/:name',
            (ctx, next) => { this.loadUserRoute(ctx, next); },
            (ctx, next) => { this.showUserRoute(ctx, next); });
        page(
            '/user/:name/edit',
            (ctx, next) => { this.loadUserRoute(ctx, next); },
            (ctx, next) => { this.editUserRoute(ctx, next); });
        page(
            '/user/:name/delete',
            (ctx, next) => { this.loadUserRoute(ctx, next); },
            (ctx, next) => { this.deleteUserRoute(ctx, next); });
        page.exit(/\/users\/.*/, (ctx, next) => {
            pageController.stop();
            next();
        });
        page.exit(/\/user\/.*/, (ctx, next) => {
            this.user = null;
            next();
        });
    }

    listUsersRoute(ctx, next) {
        topNavController.activate('users');

        pageController.run({
            state: ctx.state,
            requestPage: page => {
                return api.get(
                    '/users/?query={text}&page={page}&pageSize=30'.format({
                        text: ctx.searchQuery.text,
                        page: page}));
            },
            clientUrl: '/users/' + misc.formatSearchQuery({
                text: ctx.searchQuery.text, page: '{page}'}),
            searchQuery: ctx.searchQuery,
            headerRenderer: this.userListHeaderView,
            pageRenderer: this.userListPageView,
        });
    }

    createUserRoute() {
        topNavController.activate('register');
        this.registrationView.render({
            register: (...args) => {
                return this._register(...args);
            }});
    }

    loadUserRoute(ctx, next) {
        if (ctx.state.user) {
            next();
        } else if (this.user && this.user.name == ctx.params.name) {
            ctx.state.user = this.user;
            next();
        } else {
            api.get('/user/' + ctx.params.name).then(response => {
                response.user.rankName = rankNames[response.user.rank];
                ctx.state.user = response.user;
                ctx.save();
                this.user = response.user;
                next();
            }, response => {
                this.emptyView.render();
                events.notify(events.Error, response.description);
            });
        }
    }

    showUserRoute(ctx, next) {
        this._show(ctx.state.user, 'summary');
    }

    editUserRoute(ctx, next) {
        this._show(ctx.state.user, 'edit');
    }

    deleteUserRoute(ctx, next) {
        this._show(ctx.state.user, 'delete');
    }

    _register(name, password, email) {
        const data = {
            name: name,
            password: password,
            email: email
        };
        return new Promise((resolve, reject) => {
            api.post('/users/', data).then(() => {
                api.forget();
                return api.login(name, password, false);
            }, response => {
                return Promise.reject(response.description);
            }).then(() => {
                resolve();
                page('/');
                events.notify(events.Success, 'Welcome aboard!');
            }, errorMessage => {
                reject();
                events.notify(events.Error, errorMessage);
            });
        });
    }

    _edit(user, data) {
        const isLoggedIn = api.isLoggedIn(user);
        const infix = isLoggedIn ? 'self' : 'any';
        let files = [];

        if (!data.name) {
            delete data.name;
        }
        if (!data.password) {
            delete data.password;
        }
        if (!api.hasPrivilege('users:edit:' + infix + ':email')) {
            delete data.email;
        }
        if (!data.rank) {
            delete data.rank;
        }
        if (!data.avatarStyle ||
                (data.avatarStyle == user.avatarStyle && !data.avatarContent)) {
            delete data.avatarStyle;
        }
        if (data.avatarContent) {
            files.avatar = data.avatarContent;
        }

        return new Promise((resolve, reject) => {
            api.put('/user/' + user.name, data, files)
                .then(response => {
                    this.user = response.user;
                    return isLoggedIn ?
                        api.login(
                            data.name || api.userName,
                            data.password || api.userPassword,
                            false) :
                        Promise.fulfill();
                }, response => {
                    return Promise.reject(response.description);
                }).then(() => {
                    resolve();
                    if (data.name && data.name !== user.name) {
                        page('/user/' + data.name + '/edit');
                    }
                    events.notify(events.Success, 'Settings updated.');
                }, errorMessage => {
                    reject();
                    events.notify(events.Error, errorMessage);
                });
        });
    }

    _delete(user) {
        const isLoggedIn = api.isLoggedIn(user);
        return new Promise((resolve, reject) => {
            api.delete('/user/' + user.name)
                .then(response => {
                    if (isLoggedIn) {
                        api.forget();
                        api.logout();
                    }
                    resolve();
                    if (api.hasPrivilege('users:list')) {
                        page('/users');
                    } else {
                        page('/');
                    }
                    events.notify(events.Success, 'Account deleted');
                }, response => {
                    reject();
                    events.notify(events.Error, response.description);
                });
        });
    }

    _show(user, section) {
        const isLoggedIn = api.isLoggedIn(user);
        const infix = isLoggedIn ? 'self' : 'any';

        const myRankIdx = api.user ? api.allRanks.indexOf(api.user.rank) : 0;
        let ranks = {};
        for (let rankIdx of misc.range(api.allRanks.length)) {
            const rankIdentifier = api.allRanks[rankIdx];
            if (rankIdentifier === 'anonymous') {
                continue;
            }
            if (rankIdx > myRankIdx) {
                continue;
            }
            ranks[rankIdentifier] = Object.values(rankNames)[rankIdx];
        }

        if (isLoggedIn) {
            topNavController.activate('account');
        } else {
            topNavController.activate('users');
        }
        this.userView.render({
            user: user,
            section: section,
            isLoggedIn: isLoggedIn,
            canEditName: api.hasPrivilege('users:edit:' + infix + ':name'),
            canEditPassword: api.hasPrivilege('users:edit:' + infix + ':pass'),
            canEditEmail: api.hasPrivilege('users:edit:' + infix + ':email'),
            canEditRank: api.hasPrivilege('users:edit:' + infix + ':rank'),
            canEditAvatar: api.hasPrivilege('users:edit:' + infix + ':avatar'),
            canEditAnything: api.hasPrivilege('users:edit:' + infix),
            canDelete: api.hasPrivilege('users:delete:' + infix),
            ranks: ranks,
            edit: (...args) => { return this._edit(user, ...args); },
            delete: (...args) => { return this._delete(user, ...args); },
        });
    }
}

module.exports = new UsersController();