/* eslint max-len: ["error", { "code": 150 }]*/
const md5 = require('md5');
const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print');

module.exports = Self => {
    require('../methods/account/login')(Self);
    require('../methods/account/logout')(Self);
    require('../methods/account/acl')(Self);
    require('../methods/account/change-password')(Self);
    require('../methods/account/set-password')(Self);
    require('../methods/account/recover-password')(Self);
    require('../methods/account/validate-token')(Self);
    require('../methods/account/privileges')(Self);

    // Validations

    Self.validatesFormatOf('email', {
        message: 'Invalid email',
        allowNull: true,
        allowBlank: true,
        with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
    });

    Self.validatesUniquenessOf('name', {
        message: `A client with that Web User name already exists`
    });

    Self.observe('before save', async function(ctx) {
        if (ctx.currentInstance && ctx.currentInstance.id && ctx.data && ctx.data.password)
            ctx.data.password = md5(ctx.data.password);
    });

    Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
        if (!ctx.args || !ctx.args.data.email) return;
        const models = Self.app.models;

        const loopBackContext = LoopBackContext.getCurrentContext();
        const httpCtx = {req: loopBackContext.active};
        const httpRequest = httpCtx.req.http.req;
        const headers = httpRequest.headers;
        const origin = headers.origin;
        const url = origin.split(':');

        const userId = ctx.instance.id;
        const user = await models.user.findById(userId);

        class Mailer {
            async send(verifyOptions, cb) {
                const params = {
                    url: verifyOptions.verifyHref,
                    recipient: verifyOptions.to,
                    lang: ctx.req.getLocale()
                };

                const email = new Email('email-verify', params);
                email.send();

                cb(null, verifyOptions.to);
            }
        }

        const options = {
            type: 'email',
            to: instance.email,
            from: {},
            redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
            template: false,
            mailer: new Mailer,
            host: url[1].split('/')[2],
            port: url[2],
            protocol: url[0],
            user: Self
        };

        await user.verify(options);
    });

    Self.remoteMethod('getCurrentUserData', {
        description: 'Gets the current user data',
        accepts: [
            {
                arg: 'ctx',
                type: 'object',
                http: {source: 'context'}
            }
        ],
        returns: {
            type: 'object',
            root: true
        },
        http: {
            verb: 'GET',
            path: '/getCurrentUserData'
        }
    });

    Self.getCurrentUserData = async function(ctx) {
        let userId = ctx.req.accessToken.userId;
        return await Self.findById(userId, {
            fields: ['id', 'name', 'nickname']
        });
    };

    /**
     * Checks if user has a role.
     *
     * @param {Integer} userId The user id
     * @param {String} name The role name
     * @param {object} options Options
     * @return {Boolean} %true if user has the role, %false otherwise
     */
    Self.hasRole = async function(userId, name, options) {
        let roles = await Self.getRoles(userId, options);
        return roles.some(role => role == name);
    };

    /**
     * Get all user roles.
     *
     * @param {Integer} userId The user id
     * @param {object} options Options
     * @return {object} User role list
     */
    Self.getRoles = async(userId, options) => {
        let result = await Self.rawSql(
            `SELECT r.name
                FROM account.user u
                    JOIN account.roleRole rr ON rr.role = u.role
                    JOIN account.role r ON r.id = rr.inheritsFrom
                WHERE u.id = ?`, [userId], options);

        let roles = [];
        for (role of result)
            roles.push(role.name);

        return roles;
    };
};