/* jslint node: true */
'use strict';

//  ENiGMA½
const { MenuModule } = require('./menu_module.js');
const {
    getActiveConnectionList,
    getConnectionByNodeId,
    UserMessageableConnections,
} = require('./client_connections.js');
const UserInterruptQueue = require('./user_interrupt_queue.js');
const { getThemeArt } = require('./theme.js');
const { pipeToAnsi } = require('./color_codes.js');
const stringFormat = require('./string_format.js');
const { renderStringLength } = require('./string_util.js');
const Events = require('./events.js');

//  deps
const series = require('async/series');
const _ = require('lodash');
const async = require('async');
const moment = require('moment');

exports.moduleInfo = {
    name: 'Node Message',
    desc: 'Multi-node messaging',
    author: 'NuSkooler',
};

const FormIds = {
    sendMessage: 0,
};

const MciViewIds = {
    sendMessage: {
        nodeSelect: 1,
        message: 2,
        preview: 3,

        customRangeStart: 10,
    },
};

exports.getModule = class NodeMessageModule extends MenuModule {
    constructor(options) {
        super(options);
        this.config = Object.assign({}, _.get(options, 'menuConfig.config'), {
            extraArgs: options.extraArgs,
        });

        this.menuMethods = {
            sendMessage: (formData, extraArgs, cb) => {
                const nodeId = this.nodeList[formData.value.node].node; //  index from from -> node!
                const message = _.get(formData.value, 'message', '').trim();

                if (0 === renderStringLength(message)) {
                    return this.prevMenu(cb);
                }

                this.createInterruptItem(message, (err, interruptItem) => {
                    if (-1 === nodeId) {
                        //  ALL nodes
                        UserInterruptQueue.queue(interruptItem, { omit: this.client });
                    } else {
                        const conn = getConnectionByNodeId(nodeId);
                        if (conn) {
                            UserInterruptQueue.queue(interruptItem, { clients: conn });
                        }
                    }

                    Events.emit(Events.getSystemEvents().UserSendNodeMsg, {
                        user: this.client.user,
                        global: -1 === nodeId,
                    });

                    return this.prevMenu(cb);
                });
            },
        };
    }

    mciReady(mciData, cb) {
        super.mciReady(mciData, err => {
            if (err) {
                return cb(err);
            }

            series(
                [
                    callback => {
                        return this.prepViewController(
                            'sendMessage',
                            FormIds.sendMessage,
                            mciData.menu,
                            callback
                        );
                    },
                    callback => {
                        return this.validateMCIByViewIds(
                            'sendMessage',
                            [
                                MciViewIds.sendMessage.nodeSelect,
                                MciViewIds.sendMessage.message,
                            ],
                            callback
                        );
                    },
                    callback => {
                        const nodeSelectView = this.viewControllers.sendMessage.getView(
                            MciViewIds.sendMessage.nodeSelect
                        );
                        this.prepareNodeList();

                        nodeSelectView.on('index update', idx => {
                            this.nodeListSelectionIndexUpdate(idx);
                        });

                        nodeSelectView.setItems(this.nodeList);
                        nodeSelectView.redraw();
                        this.nodeListSelectionIndexUpdate(0);
                        return callback(null);
                    },
                    callback => {
                        const previewView = this.viewControllers.sendMessage.getView(
                            MciViewIds.sendMessage.preview
                        );
                        if (!previewView) {
                            return callback(null); //  preview is optional
                        }

                        const messageView = this.viewControllers.sendMessage.getView(
                            MciViewIds.sendMessage.message
                        );
                        let timerId;
                        messageView.on(
                            'key press',
                            () => {
                                clearTimeout(timerId);
                                const focused =
                                    this.viewControllers.sendMessage.getFocusedView();
                                if (focused === messageView) {
                                    previewView.setText(messageView.getData());
                                    focused.setFocus(true);
                                }
                            },
                            500
                        );
                    },
                ],
                err => {
                    return cb(err);
                }
            );
        });
    }

    createInterruptItem(message, cb) {
        const dateTimeFormat =
            this.config.dateTimeFormat ||
            this.client.currentTheme.helpers.getDateTimeFormat();

        const textFormatObj = {
            fromUserName: this.client.user.username,
            fromRealName: this.client.user.properties.real_name,
            fromNodeId: this.client.node,
            message: message,
            timestamp: moment().format(dateTimeFormat),
        };

        const messageFormat =
            this.config.messageFormat ||
            'Message from {fromUserName} on node {fromNodeId}:\r\n{message}';

        const item = {
            text: stringFormat(messageFormat, textFormatObj),
            pause: true,
        };

        const getArt = (name, callback) => {
            const spec = _.get(this.config, `art.${name}`);
            if (!spec) {
                return callback(null);
            }
            const getArtOpts = {
                name: spec,
                client: this.client,
                random: false,
            };
            getThemeArt(getArtOpts, (err, artInfo) => {
                //  ignore errors
                return callback(artInfo ? artInfo.data : null);
            });
        };

        async.waterfall(
            [
                callback => {
                    getArt('header', headerArt => {
                        return callback(null, headerArt);
                    });
                },
                (headerArt, callback) => {
                    getArt('footer', footerArt => {
                        return callback(null, headerArt, footerArt);
                    });
                },
                (headerArt, footerArt, callback) => {
                    if (headerArt || footerArt) {
                        item.contents = `${headerArt || ''}\r\n${pipeToAnsi(
                            item.text
                        )}\r\n${footerArt || ''}`;
                    }
                    return callback(null);
                },
            ],
            err => {
                return cb(err, item);
            }
        );
    }

    prepareNodeList() {
        //  standard node list with {text} field added for compliance
        this.nodeList = [
            {
                text: '-ALL-',
                //  dummy fields:
                node: -1,
                authenticated: false,
                userId: 0,
                action: 'N/A',
                userName: 'Everyone',
                realName: 'All Users',
                location: 'N/A',
                affils: 'N/A',
                timeOn: 'N/A',
            },
        ]
            .concat(
                getActiveConnectionList(UserMessageableConnections).map(node =>
                    Object.assign(node, {
                        text: -1 == node.node ? '-ALL-' : node.node.toString(),
                    })
                )
            )
            .filter(node => node.node !== this.client.node); //  remove our client's node
        this.nodeList.sort((a, b) => a.node - b.node); //  sort by node
    }

    nodeListSelectionIndexUpdate(idx) {
        const node = this.nodeList[idx];
        if (!node) {
            return;
        }
        this.updateCustomViewTextsWithFilter(
            'sendMessage',
            MciViewIds.sendMessage.customRangeStart,
            node
        );
    }
};
