/**
 * @module converse-chatboxes
 * @copyright 2020, the Converse.js contributors
 * @license Mozilla Public License (MPLv2)
 */
import "./converse-emoji";
import { Collection } from "@converse/skeletor/src/collection";
import { _converse, api, converse } from "./converse-core";
import { isString } from "lodash-es";
import log from "./log";

const { Strophe } = converse.env;

Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');


converse.plugins.add('converse-chatboxes', {

    dependencies: ["converse-emoji", "converse-roster", "converse-vcard"],

    initialize () {
        /* The initialize function gets called as soon as the plugin is
         * loaded by converse.js's plugin machinery.
         */

        api.promises.add([
            'chatBoxesFetched',
            'chatBoxesInitialized',
            'privateChatsAutoJoined'
        ]);

        let msg_counter = 0;

        _converse.incrementMsgCounter = function () {
            msg_counter += 1;
            const title = document.title;
            if (!title) {
                return;
            }
            if (title.search(/^Messages \(\d+\) /) === -1) {
                document.title = `Messages (${msg_counter}) ${title}`;
            } else {
                document.title = title.replace(/^Messages \(\d+\) /, `Messages (${msg_counter}) `);
            }
        };

        _converse.clearMsgCounter = function () {
            msg_counter = 0;
            const title = document.title;
            if (!title) {
                return;
            }
            if (title.search(/^Messages \(\d+\) /) !== -1) {
                document.title = title.replace(/^Messages \(\d+\) /, "");
            }
        };


        _converse.ChatBoxes = Collection.extend({
            comparator: 'time_opened',

            model (attrs, options) {
                return new _converse.ChatBox(attrs, options);
            },

            onChatBoxesFetched (collection) {
                collection.filter(c => !c.isValid()).forEach(c => c.destroy());
                /**
                 * Triggered when a message stanza is been received and processed.
                 * @event _converse#chatBoxesFetched
                 * @type { object }
                 * @property { _converse.ChatBox | _converse.ChatRoom } chatbox
                 * @property { XMLElement } stanza
                 * @example _converse.api.listen.on('message', obj => { ... });
                 * @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
                 */
                api.trigger('chatBoxesFetched');
            },

            onConnected (reconnecting) {
                if (reconnecting) { return; }
                this.browserStorage = _converse.createStore(`converse.chatboxes-${_converse.bare_jid}`);
                this.fetch({
                    'add': true,
                    'success': c => this.onChatBoxesFetched(c)
                });
            }
        });


        async function createChatBox (jid, attrs, Model) {
            jid = Strophe.getBareJidFromJid(jid.toLowerCase());
            Object.assign(attrs, {'jid': jid, 'id': jid});
            let chatbox;
            try {
                chatbox = new Model(attrs, {'collection': _converse.chatboxes});
            } catch (e) {
                log.error(e);
                return null;
            }
            await chatbox.initialized;
            if (!chatbox.isValid()) {
                chatbox.destroy();
                return null;
            }
            _converse.chatboxes.add(chatbox);
            await chatbox.messages.fetched;
            return chatbox;
        }


        /************************ BEGIN Event Handlers ************************/
        api.listen.on('addClientFeatures', () => {
            api.disco.own.features.add(Strophe.NS.MESSAGE_CORRECT);
            api.disco.own.features.add(Strophe.NS.HTTPUPLOAD);
            api.disco.own.features.add(Strophe.NS.OUTOFBAND);
        });

        api.listen.on('pluginsInitialized', () => {
            _converse.chatboxes = new _converse.ChatBoxes();
            /**
             * Triggered once the _converse.ChatBoxes collection has been initialized.
             * @event _converse#chatBoxesInitialized
             * @example _converse.api.listen.on('chatBoxesInitialized', () => { ... });
             * @example _converse.api.waitUntil('chatBoxesInitialized').then(() => { ... });
             */
            api.trigger('chatBoxesInitialized');
        });

        api.listen.on('presencesInitialized', (reconnecting) => _converse.chatboxes.onConnected(reconnecting));
        api.listen.on('reconnected', () => _converse.chatboxes.forEach(m => m.onReconnection()));
        api.listen.on('windowStateChanged', d => (d.state === 'visible') && _converse.clearMsgCounter());
        /************************ END Event Handlers ************************/


        /************************ BEGIN API ************************/
        Object.assign(api, {
            /**
             * The "chatboxes" namespace.
             *
             * @namespace api.chatboxes
             * @memberOf api
             */
            chatboxes: {
                /**
                 * @method api.chats.create
                 * @param { String|String[] } jids - A JID or array of JIDs
                 * @param { Object } [attrs] An object containing configuration attributes
                 * @param { Model } model - The type of chatbox that should be created
                 */
                async create (jids=[], attrs={}, model) {
                    await api.waitUntil('chatBoxesFetched');
                    if (isString(jids)) {
                        return createChatBox(jids, attrs, model);
                    } else {
                        return Promise.all(jids.map(jid => createChatBox(jid, attrs, model)));
                    }
                },

                /**
                 * @method api.chats.get
                 * @param { String|String[] } jids - A JID or array of JIDs
                 */
                async get (jids) {
                    await api.waitUntil('chatBoxesFetched');
                    if (jids === undefined) {
                        return _converse.chatboxes.models;
                    } else if (isString(jids)) {
                        return _converse.chatboxes.get(jids.toLowerCase());
                    } else {
                        jids = jids.map(j => j.toLowerCase());
                        return _converse.chatboxes.models.filter(m => jids.includes(m.get('jid')));
                    }
                }
            }
        });
        /************************ END API ************************/
    }
});
