import ChatRoomOccupant from './occupant.js';
import u from '../../utils/form';
import { Collection } from '@converse/skeletor/src/collection';
import { Strophe } from 'strophe.js/src/strophe';
import { _converse, api } from '../../core.js';

const MUC_ROLE_WEIGHTS = {
    'moderator': 1,
    'participant': 2,
    'visitor': 3,
    'none': 2
};


/**
 * A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC.
 * @class
 * @namespace _converse.ChatRoomOccupants
 * @memberOf _converse
 */
const ChatRoomOccupants = Collection.extend({
    model: ChatRoomOccupant,

    comparator (occupant1, occupant2) {
        const role1 = occupant1.get('role') || 'none';
        const role2 = occupant2.get('role') || 'none';
        if (MUC_ROLE_WEIGHTS[role1] === MUC_ROLE_WEIGHTS[role2]) {
            const nick1 = occupant1.getDisplayName().toLowerCase();
            const nick2 = occupant2.getDisplayName().toLowerCase();
            return nick1 < nick2 ? -1 : nick1 > nick2 ? 1 : 0;
        } else {
            return MUC_ROLE_WEIGHTS[role1] < MUC_ROLE_WEIGHTS[role2] ? -1 : 1;
        }
    },

    getAutoFetchedAffiliationLists () {
        const affs = api.settings.get('muc_fetch_members');
        return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : [];
    },

    async fetchMembers () {
        const affiliations = this.getAutoFetchedAffiliationLists();
        if (affiliations.length === 0) {
            return;
        }
        const aff_lists = await Promise.all(affiliations.map(a => this.chatroom.getAffiliationList(a)));
        const new_members = aff_lists.reduce((acc, val) => (u.isErrorObject(val) ? acc : [...val, ...acc]), []);
        const known_affiliations = affiliations.filter(
            a => !u.isErrorObject(aff_lists[affiliations.indexOf(a)])
        );
        const new_jids = new_members.map(m => m.jid).filter(m => m !== undefined);
        const new_nicks = new_members.map(m => (!m.jid && m.nick) || undefined).filter(m => m !== undefined);
        const removed_members = this.filter(m => {
            return (
                known_affiliations.includes(m.get('affiliation')) &&
                !new_nicks.includes(m.get('nick')) &&
                !new_jids.includes(m.get('jid'))
            );
        });

        removed_members.forEach(occupant => {
            if (occupant.get('jid') === _converse.bare_jid) {
                return;
            }
            if (occupant.get('show') === 'offline') {
                occupant.destroy();
            } else {
                occupant.save('affiliation', null);
            }
        });
        new_members.forEach(attrs => {
            const occupant = attrs.jid
                ? this.findOccupant({ 'jid': attrs.jid })
                : this.findOccupant({ 'nick': attrs.nick });
            if (occupant) {
                occupant.save(attrs);
            } else {
                this.create(attrs);
            }
        });
        /**
         * Triggered once the member lists for this MUC have been fetched and processed.
         * @event _converse#membersFetched
         * @example _converse.api.listen.on('membersFetched', () => { ... });
         */
        api.trigger('membersFetched');
    },

    /**
     * @typedef { Object} OccupantData
     * @property { String } [jid]
     * @property { String } [nick]
     */
    /**
     * Try to find an existing occupant based on the passed in
     * data object.
     *
     * If we have a JID, we use that as lookup variable,
     * otherwise we use the nick. We don't always have both,
     * but should have at least one or the other.
     * @private
     * @method _converse.ChatRoomOccupants#findOccupant
     * @param { OccupantData } data
     */
    findOccupant (data) {
        const jid = Strophe.getBareJidFromJid(data.jid);
        return (jid && this.findWhere({ jid })) || this.findWhere({ 'nick': data.nick });
    }
});


export default ChatRoomOccupants;
