/**
 * @module converse-modal
 * @copyright The Converse.js contributors
 * @license Mozilla Public License (MPLv2)
 */
import Alert from '../modals/alert.js';
import BootstrapModal from '../modals/base.js';
import Confirm from '../modals/confirm.js';
import { Model } from '@converse/skeletor/src/model.js';
import { _converse, api, converse } from "@converse/headless/core";


converse.env.BootstrapModal = BootstrapModal; // expose to plugins


let modals = [];


const modal_api = {

    /**
     * API namespace for methods relating to modals
     * @namespace _converse.api.modal
     * @memberOf _converse.api
     */
    modal: {
        /**
         * Shows a modal of type `ModalClass` to the user.
         * Will create a new instance of that class if an existing one isn't
         * found.
         * @param { Class } ModalClass
         * @param { Object } [properties] - Optional properties that will be
         *  set on a newly created modal instance (if no pre-existing modal was
         *  found).
         * @param { Event } [event] - The DOM event that causes the modal to be shown.
         */
        show (ModalClass, properties, ev) {
            const modal = this.get(ModalClass.id) || this.create(ModalClass, properties);
            modal.show(ev);
            return modal;
        },

        /**
         * Return a modal with the passed-in identifier, if it exists.
         * @param { String } id
         */
        get (id) {
            return modals.filter(m => m.id == id).pop();
        },

        /**
         * Create a modal of the passed-in type.
         * @param { Class } ModalClass
         * @param { Object } [properties] - Optional properties that will be
         *  set on the modal instance.
         */
        create (ModalClass, properties) {
            const modal = new ModalClass(properties);
            modals.push(modal);
            return modal;
        },

        /**
         * Remove a particular modal
         * @param { View } modal
         */
        remove (modal) {
            modals = modals.filter(m => m !== modal);
            modal.remove();
        },

        /**
         * Remove all modals
         */
        removeAll () {
            modals.forEach(m => m.remove());
            modals = [];
        }
    },

    /**
     * Show a confirm modal to the user.
     * @method _converse.api.confirm
     * @param { String } title - The header text for the confirmation dialog
     * @param { (String[]|String) } messages - The text to show to the user
     * @param { Array<Field> } fields - An object representing a fields presented to the user.
     * @property { String } Field.label - The form label for the input field.
     * @property { String } Field.name - The name for the input field.
     * @property { String } [Field.challenge] - A challenge value that must be provided by the user.
     * @property { String } [Field.placeholder] - The placeholder for the input field.
     * @property { Boolean} [Field.required] - Whether the field is required or not
     * @returns { Promise<Array|false> } A promise which resolves with an array of
     *  filled in fields or `false` if the confirm dialog was closed or canceled.
     */
    async confirm (title, messages=[], fields=[]) {
        if (typeof messages === 'string') {
            messages = [messages];
        }
        const model = new Model({title, messages, fields, 'type': 'confirm'})
        const confirm = new Confirm({model});
        confirm.show();
        let result;
        try {
            result = await confirm.confirmation;
        } catch (e) {
            result = false;
        }
        confirm.remove();
        return result;
    },

    /**
     * Show a prompt modal to the user.
     * @method _converse.api.prompt
     * @param { String } title - The header text for the prompt
     * @param { (String[]|String) } messages - The prompt text to show to the user
     * @param { String } placeholder - The placeholder text for the prompt input
     * @returns { Promise<String|false> } A promise which resolves with the text provided by the
     *  user or `false` if the user canceled the prompt.
     */
    async prompt (title, messages=[], placeholder='') {
        if (typeof messages === 'string') {
            messages = [messages];
        }
        const model = new Model({
            title,
            messages,
            'fields': [{
                'name': 'reason',
                'placeholder': placeholder,
            }],
            'type': 'prompt'
        })
        const prompt = new Confirm({model});
        prompt.show();
        let result;
        try {
            result = (await prompt.confirmation).pop()?.value;
        } catch (e) {
            result = false;
        }
        prompt.remove();
        return result;
    },

    /**
     * Show an alert modal to the user.
     * @method _converse.api.alert
     * @param { ('info'|'warn'|'error') } type - The type of alert.
     * @param { String } title - The header text for the alert.
     * @param { (String[]|String) } messages - The alert text to show to the user.
     */
    alert (type, title, messages) {
        if (typeof messages === 'string') {
            messages = [messages];
        }
        let level;
        if (type === 'error') {
            level = 'alert-danger';
        } else if (type === 'info') {
            level = 'alert-info';
        } else if (type === 'warn') {
            level = 'alert-warning';
        }

        const model = new Model({
            'title': title,
            'messages': messages,
            'level': level,
            'type': 'alert'
        })
        api.modal.show(Alert, {model});
    }
}


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

    initialize () {
        api.listen.on('disconnect', () => {
            const container = document.querySelector("#converse-modals");
            if (container) {
                container.innerHTML = '';
            }
        });

        api.listen.on('clearSession', () => api.modal.removeAll());

        Object.assign(_converse.api, modal_api);
    }
});
