const request = require('request-promise-native');
module.exports = Self => {
    Self.remoteMethodCtx('send', {
        description: 'Send a RocketChat message',
        accessType: 'WRITE',
        accepts: [{
            arg: 'to',
            type: 'String',
            required: true,
            description: 'User (@) or channel (#) to send the message'
        }, {
            arg: 'message',
            type: 'String',
            required: true,
            description: 'The message'
        }],
        returns: {
            type: 'Object',
            root: true
        },
        http: {
            path: `/send`,
            verb: 'POST'
        }
    });

    Self.send = async(ctx, to, message) => {
        const models = Self.app.models;
        const accessToken = ctx.req.accessToken;
        const sender = await models.Account.findById(accessToken.userId);
        const recipient = to.replace('@', '');

        if (sender.name != recipient)
            return sendMessage(sender, to, `@${sender.name}: ${message} `);
    };

    async function sendMessage(sender, channel, message) {
        const config = await getConfig();
        const avatar = `${config.host}/avatar/${sender.name}`;
        const uri = `${config.api}/chat.postMessage`;

        return sendAuth(uri, {
            'channel': channel,
            'avatar': avatar,
            'text': message
        }).catch(async error => {
            if (error.statusCode === 401 && !this.resendAttempted) {
                this.resendAttempted = true;

                return sendMessage(sender, channel, message);
            }

            throw new Error(error.message);
        });
    }

    /**
     * Returns a rocketchat token
     * @return {Object} userId and authToken
     */
    async function getAuthToken() {
        if (!this.auth || this.auth && !this.auth.authToken) {
            const config = await getConfig();
            const uri = `${config.api}/login`;
            const res = await send(uri, {
                user: config.user,
                password: config.password
            });

            this.auth = res.data;
        }

        return this.auth;
    }

    /**
     * Returns a rocketchat config
     * @return {Object} Auth config
     */
    async function getConfig() {
        if (!this.chatConfig) {
            const models = Self.app.models;

            this.chatConfig = await models.ChatConfig.findOne();
        }

        return this.chatConfig;
    }

    /**
     * Send unauthenticated request
     * @param {*} uri - Request uri
     * @param {*} body - Request params
     * @param {*} options - Request options
     *
     * @return {Object} Request response
     */
    async function send(uri, body, options) {
        if (process.env.NODE_ENV !== 'production') {
            return new Promise(resolve => {
                return resolve({statusCode: 200, message: 'Fake notification sent'});
            });
        }

        const defaultOptions = {
            method: 'POST',
            uri: uri,
            body: body,
            headers: {'content-type': 'application/json'},
            json: true
        };

        if (options) Object.assign(defaultOptions, options);

        return request(defaultOptions);
    }

    /**
     * Send authenticated request
     * @param {*} uri - Request uri
     * @param {*} body - Request params
     *
     * @return {Object} Request response
     */
    async function sendAuth(uri, body) {
        const login = await getAuthToken();
        const options = {
            headers: {'content-type': 'application/json'}
        };

        if (login) {
            options.headers['X-Auth-Token'] = login.authToken;
            options.headers['X-User-Id'] = login.userId;
        }

        return send(uri, body, options);
    }
};