#include csrfp.js
#include msgbox.js
#include utility.js
#include messages/actbtn.js
#include messages/list.js
#include messages/recipient.js
#include messages/reply.jsx
#include messages/thread.js

const MszMessages = () => {
    const extractMsgIds = msg => {
        if(typeof msg.getId === 'function')
            return msg.getId();
        if(typeof msg.toString === 'function')
            return msg.toString();
        throw 'unsupported message type';
    };

    const displayErrorMessage = async error => {
        let text;
        if(typeof error === 'string')
            text = error;
        else if(typeof error.text === 'string')
            text = error.text;
        else if(typeof error.toString === 'function')
            text = error.toString();
        else
            text = 'Something indescribable happened.';

        await MszShowMessageBox(text, 'Error');
        return false;
    };

    const msgsCreate = async (title, text, parser, draft, recipient, replyTo) => {
        const formData = new FormData;
        formData.append('_csrfp', MszCSRFP.getToken());
        formData.append('title', title);
        formData.append('body', text);
        formData.append('parser', parser);
        formData.append('draft', draft);
        formData.append('recipient', recipient);
        formData.append('reply', replyTo);

        const result = await $x.post('/messages/create', { type: 'json' }, formData);

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return body;
    };

    const msgsUpdate = async (messageId, title, text, parser, draft) => {
        const formData = new FormData;
        formData.append('_csrfp', MszCSRFP.getToken());
        formData.append('title', title);
        formData.append('body', text);
        formData.append('parser', parser);
        formData.append('draft', draft);

        const result = await $x.post(`/messages/${encodeURIComponent(messageId)}`, { type: 'json' }, formData);

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return body;
    };

    const msgsMark = async (msgs, state) => {
        const result = await $x.post('/messages/mark', { type: 'json' }, {
            _csrfp: MszCSRFP.getToken(),
            type: state,
            messages: msgs.map(extractMsgIds).join(','),
        });

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return true;
    };

    const msgsDelete = async msgs => {
        const result = await $x.post('/messages/delete', { type: 'json' }, {
            _csrfp: MszCSRFP.getToken(),
            messages: msgs.map(extractMsgIds).join(','),
        });

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return true;
    };

    const msgsRestore = async msgs => {
        const result = await $x.post('/messages/restore', { type: 'json' }, {
            _csrfp: MszCSRFP.getToken(),
            messages: msgs.map(extractMsgIds).join(','),
        });

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return true;
    };

    const msgsNuke = async msgs => {
        const result = await $x.post('/messages/nuke', { type: 'json' }, {
            _csrfp: MszCSRFP.getToken(),
            messages: msgs.map(extractMsgIds).join(','),
        });

        MszCSRFP.setFromHeaders(result);

        const body = result.body();
        if(body.error !== undefined)
            throw body.error;

        return true;
    };

    const msgsUserBtns = Array.from($qa('.js-header-pms-button'));
    if(msgsUserBtns.length > 0)
        $x.get('/messages/stats', { type: 'json' }).then(result => {
            const body = result.body();
            if(typeof body === 'object' && typeof body.unread === 'number')
                if(body.unread > 0)
                    for(const msgsUserBtn of msgsUserBtns)
                        msgsUserBtn.append($e({ child: body.unread.toLocaleString(), attrs: { className: 'header__desktop__user__button__count' } }));
        });

    const msgsListElem = $q('.js-messages-list');
    const msgsList = msgsListElem instanceof Element ? new MsgMessagesList(msgsListElem) : undefined;

    const msgsListEmptyNotice = $q('.js-messages-folder-empty');

    const msgsThreadElem = $q('.js-messages-thread');
    const msgsThread = msgsThreadElem instanceof Element ? new MszMessagesThread(msgsThreadElem) : undefined;

    const msgsRecipientElem = $q('.js-messages-recipient');
    const msgsRecipient = msgsRecipientElem instanceof Element ? new MszMessagesRecipient(msgsRecipientElem) : undefined;

    const msgsReplyElem = $q('.js-messages-reply');
    const msgsReply = msgsReplyElem instanceof Element ? new MszMessagesReply(msgsReplyElem) : undefined;

    if(msgsReply !== undefined) {
        if(msgsRecipient !== undefined)
            msgsRecipient.onUpdate(async info => {
                msgsReply.setRecipient(typeof info.id === 'string' ? info.id : '');
                msgsReply.setWarning(info.ban ? `${(typeof info.name === 'string' ? info.name : 'This user')} has been banned and will be unable to respond to your messages.` : undefined);
            });

        msgsReply.onSubmit(async form => {
            try {
                let result;
                if(typeof form.message === 'string') {
                    result = await msgsUpdate(
                        form.message,
                        form.title,
                        form.body,
                        form.parser,
                        form.draft
                    );
                } else {
                    result = await msgsCreate(
                        form.title,
                        form.body,
                        form.parser,
                        form.draft,
                        form.recipient,
                        form.reply || ''
                    );
                }

                if(typeof result.url === 'string')
                    location.assign(result.url);
            } catch(ex) {
                return await displayErrorMessage(ex);
            }
        });
    }

    let actSelectAll, actMarkRead, actMoveTrash, actNuke;

    const actSelectAllBtn = $q('.js-messages-actions-select-all');
    if(actSelectAllBtn instanceof Element) {
        actSelectAll = new MszMessagesActionButton(actSelectAllBtn);

        if(msgsList !== undefined) {
            actSelectAll.setAction(async state => {
                msgsList.setAllSelected(!state);
                return !state;
            });
            msgsList.onSelectedChange((selectedNo, itemNo) => {
                actSelectAll.setState(selectedNo >= itemNo);
            });
            actSelectAll.setState(msgsList.getAllSelected());
        }
    }

    const actMarkReadBtn = $q('.js-messages-actions-mark-read');
    if(actMarkReadBtn instanceof Element) {
        actMarkRead = new MszMessagesActionButton(actMarkReadBtn);

        if(msgsList !== undefined) {
            msgsList.onSelectedChange(selectedNo => {
                const enabled = selectedNo > 0;
                actMarkRead.setEnabled(enabled);

                if(enabled) {
                    const items = msgsList.getSelectedItems();
                    let readNo = 0, unreadNo = 0;

                    for(const item of items) {
                        if(item.isRead())
                            ++readNo;
                        else
                            ++unreadNo;
                    }

                    actMarkRead.setState(readNo > unreadNo);
                }
            });
            actMarkRead.setAction(async state => {
                const items = msgsList.getSelectedItems();

                const result = await actMarkRead.disableWith(async () => {
                    try {
                        return await msgsMark(items, state ? 'unread' : 'read');
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                if(result) {
                    state = !state;

                    for(const item of items)
                        item.setRead(state);

                    return state;
                }
            });
        } else if(msgsThread !== undefined) {
            actMarkRead.setAction(async state => {
                const items = [msgsThread.getMessage()];

                const result = await actMarkRead.disableWith(async () => {
                    try {
                        return await msgsMark(items, state ? 'unread' : 'read');
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                return result ? !state : state;
            });
        }
    }

    const actMoveTrashBtn = $q('.js-messages-actions-move-trash');
    if(actMoveTrashBtn instanceof Element) {
        actMoveTrash = new MszMessagesActionButton(actMoveTrashBtn);

        if(msgsList !== undefined) {
            msgsList.onSelectedChange(selectedNo => actMoveTrash.setEnabled(selectedNo > 0));
            actMoveTrash.setAction(async state => {
                const items = msgsList.getSelectedItems();

                if(!state && !await MszShowConfirmBox(`Are you sure you wish to delete ${items.length} item${items.length === 1 ? '' : 's'}?`, 'Confirmation'))
                    return;

                const result = await actMoveTrash.disableWith(async () => {
                    try {
                        if(state)
                            return await msgsRestore(items);
                        return await msgsDelete(items);
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                if(result)
                    for(const message of items)
                        msgsList.removeItem(message);

                if(msgsListEmptyNotice instanceof Element)
                    msgsListEmptyNotice.hidden = msgsList.getItemsCount() > 0;
            });
        } else if(msgsThread !== undefined) {
            actMoveTrash.setAction(async state => {
                if(!state && !await MszShowConfirmBox('Are you sure you wish to delete this message?', 'Confirmation'))
                    return;

                const items = [msgsThread.getMessage()];

                const result = await actMoveTrash.disableWith(async () => {
                    try {
                        if(state)
                            return await msgsRestore(items);
                        return await msgsDelete(items);
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                if(result) {
                    state = !state;

                    if(msgsReply !== undefined)
                        msgsReply.setHidden(state);

                    const msg = msgsThread.getMessage();
                    if(msg !== undefined)
                        msg.setDeleted(state);

                    return state;
                }
            });
        }
    }

    const actNukeBtn = $q('.js-messages-actions-nuke');
    if(actNukeBtn instanceof Element) {
        actNuke = new MszMessagesActionButton(actNukeBtn, true);

        if(msgsList !== undefined) {
            msgsList.onSelectedChange(selectedNo => actNuke.setEnabled(selectedNo > 0));
            actNuke.setAction(async () => {
                const items = msgsList.getSelectedItems();

                if(!await MszShowConfirmBox(`Are you sure you wish to PERMANENTLY delete ${items.length} item${items.length === 1 ? '' : 's'}?`, 'Confirmation'))
                    return;

                const result = await actNuke.disableWith(async () => {
                    try {
                        return await msgsNuke(items);
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                if(result)
                    for(const message of items)
                        msgsList.removeItem(message);

                if(msgsListEmptyNotice instanceof Element)
                    msgsListEmptyNotice.hidden = msgsList.getItemsCount() > 0;
            });
        } else if(msgsThread !== undefined) {
            actMoveTrash.watchState(state => {
                actNuke.setHidden(!state);
            });
            actNuke.setAction(async () => {
                if(!await MszShowConfirmBox('Are you sure you wish to PERMANENTLY delete this message?', 'Confirmation'))
                    return;

                const items = [msgsThread.getMessage()];

                const result = await actNuke.disableWith(async () => {
                    try {
                        return await msgsNuke(items);
                    } catch(ex) {
                        return await displayErrorMessage(ex);
                    }
                });

                if(result)
                    location.assign('/messages');
            });
        }
    }
};
