// 
/// <reference types="../../.typings/browser" />
/// <reference types="../../.typings/mailmindr" />
import {
    createMailmindrId,
    createMindrFromActionTemplate,
    createRemindMeMinutesBefore,
    createSystemActions,
    createSystemTimespans,
    findThunderbirdVersion,
    genericFolderToLocalFolder,
    getGenericIceboxFolderForIdentityOrNull,
    localFolderToGenericFolder,
    sanitizeHeaderMessageId,
    sendConnectionMessageEx,
    showReminderForMindr,
    throttle
} from '../modules/core-utils.mjs.js';
import {
    getCurrentStorageAdapterVersion,
    getStorageAdapter
} from '../modules/storage.mjs.js';
import { createCorrelationId, createLogger } from '../modules/logger.mjs.js';
import { createStore } from '../modules/store/state-manager.mjs.js';
import { initialState } from '../modules/defaults.mjs.js';
import {
    closeDialog,
    connectionClosed,
    connectionOpened,
    createOrUpdateDraft,
    createOrUpdateMindr,
    createTimespanPreset,
    heartBeat,
    heartBeatEx,
    openDialog,
    removeDraft,
    removeMindr,
    removeTimespanPreset,
    setReplyReceived,
    showMindrAlert,
    snoozeMindrs,
    updateSetting,
    updateTimespanPreset
} from '../modules/store/actions/index.mjs.js';
import {
    selectDialogForType,
    selectDraftForSenderTabOrNull,
    selectMindrByGuid,
    selectMindrByHeaderMessageId,
    selectMindrs,
    selectOpenConnections,
    selectOpenDialogs,
    selectPresets,
    selectSettings
} from '../modules/store/selectors/index.mjs.js';
import {
    getMessages,
    moveMessageToFolder
} from '../modules/message-utils.mjs.js';
import rootReducer from '../modules/store/reducers/index.mjs.js';

const logger = createLogger('background');

let store; //  = createStore(() => {}, initialState);

const createInitialSettings = (presets, _settings) => ({
    snoozeTime: 15, // 15 minutes is initial default,
    iceboxFolders: [], // we don't have any icebox folders
    defaultIceboxFolder: null, // we don't set a folder by default
    defaultTimePreset:
        presets.time?.[presets.time?.find(item => item.isSelectable) || 0], // get first preset (if any)
    defaultActionPreset:
        presets.actions?.[
            presets.actions?.find(item => item.isSelectable) || 0
        ], // get first preset (if any)
    defaultRemindMeMinutesBefore: 15
});

const createSystemPresets = () => ({
    time: createSystemTimespans(),
    actions: createSystemActions(),
    remindMeMinutesBefore: createRemindMeMinutesBefore()
});

const getStorage = async () => {
    // 
    const storage = await browser.storage.local.get(null);
    const storageVersion = storage?.storageVersion || 1;

    // 
    const { loadState, storeState } = getStorageAdapter(storageVersion);
    return { loadState, storeState, storageVersion };
};

const initializeStorage = async () => {
    try {
        const {
            loadState,
            storeState,
            storageVersion: persistedStorageVersion
        } = await getStorage();
        const storageVersion = getCurrentStorageAdapterVersion();

        // 
        if (storageVersion > persistedStorageVersion) {
            // 
            const { loadState: loadLegacyState } = getStorageAdapter(
                persistedStorageVersion
            );

            // 
            const legacyState = await loadLegacyState();

            logger.warn(`legacy storage migrated: `, legacyState);

            // 
            await storeState(legacyState);
        }

        // 
        const restoredState = await loadState();

        // 
        const {
            time: systemGeneratedTimePresets,
            actions,
            remindMeMinutesBefore
        } = createSystemPresets();
        const defaultSettings = createInitialSettings({
            systemGeneratedTimePresets,
            actions
        });

        // 
        const settings = {
            ...defaultSettings,
            ...restoredState.settings
        };

        // 
        const userGeneratedTimePresets = (
            restoredState?.presets?.time || []
        ).filter(item => !item.isGenerated && item.isSelectable);
        const time = [
            ...systemGeneratedTimePresets,
            ...userGeneratedTimePresets
        ];

        // 
        const localState = {
            ...restoredState,
            settings,
            presets: {
                time,
                actions,
                remindMeMinutesBefore
            },
            overdue: [], // can be computed
            active: [], // can be computed
            openDialogs: [], // can be computed
            openConnections: [], // can be computed
            __inExecution: []
        };

        logger.debug(`restored state: `, localState);

        return localState;
    } catch (exception) {
        logger.error(
            `We're having a problem on storage initialization`,
            exception
        );
        return null;
    }
};

const onHeartBeatHandler = async () => {
    store.dispatch(heartBeat());
    store.dispatch(heartBeatEx());
};

const startHeartbeat = () => {
    setInterval(onHeartBeatHandler, 1000 * 45);
};

const setMindrForCurrentMessage = async optionalCurrentMessage => {
    const current = await browser.tabs.query({
        currentWindow: true,
        active: true
    });
    if (current && Array.isArray(current) && current.length > 0) {
        const { id, windowId } = current[0];
        const mindr = await findExistingMindrForTab(
            { id, windowId },
            optionalCurrentMessage
        );
        const currentMessage =
            optionalCurrentMessage ||
            (await getCurrentDisplayedMessage({
                id,
                windowId
            }));

        if (currentMessage) {
            await showCreateOrUpdateMindrDialog(currentMessage, mindr);
        }
    } else {
        logger.warn('☠️ The current message cannot be determined.');
    }
};

const setupUI = () => {
    /* intentionally left blank */
};

const sendConnectionMessage = async (connectionName, message) => {
    const state = store.getState();
    const openConnections = selectOpenConnections(state);
    await sendConnectionMessageEx(openConnections, message, connectionName);
};

const editMindrByGuid = async guid => {
    const correlationId = createCorrelationId('editMindrByGuid');
    logger.log('BEGIN editMindrByGuid', { correlationId, guid });
    const state = store.getState();
    const mindr = selectMindrByGuid(state, guid);
    const {
        headerMessageId,
        author,
        subject,
        folderAccountId,
        folderName,
        folderPath,
        folderType,
        folderAccountIdentityMailAddress
    } = mindr.metaData;
    const folder = {
        accountId: folderAccountId,
        name: folderName,
        path: folderPath,
        type: folderType
    };
    const message = {
        headerMessageId,
        author,
        folder,
        subject
    };
    await showCreateOrUpdateMindrDialog(message, mindr);
    logger.log('END editMindrByGuid', { correlationId, guid });
};

const bringDialogToFront = async dialogType => {
    const state = store.getState();
    const dialog = selectDialogForType(state, dialogType);

    if (!dialog) {
        return false;
    }

    await messenger.windows.update(dialog.details.id, {
        focused: true
    });

    return true;
};

const showCreateOrUpdateMindrDialog = async (currentMessage, mindr) => {
    const dialogType = 'mailmindr:dialog:set-mindr';
    const { headerMessageId, author, folder, subject } = currentMessage;
    const genericFolder = await localFolderToGenericFolder(folder);

    logger.info('generic folder', genericFolder);

    const { accountId, name, path, type, identityEmailAddress } = genericFolder;
    const dialogId = createMailmindrId('mailmindr:dialog:set-mindr');

    const parameters = new URLSearchParams();
    parameters.set('dialogId', dialogId);
    parameters.set('headerMessageId', headerMessageId);
    parameters.set('author', author);
    parameters.set('subject', subject);

    if (mindr) {
        parameters.set('guid', mindr.guid);
    }

    parameters.set('folderAccountId', accountId);
    parameters.set('folderName', name || '');
    parameters.set('folderPath', path || '');
    parameters.set('folderType', type || '');
    parameters.set(
        'folderAccountIdentityMailAddress',
        identityEmailAddress || ''
    );

    const url = `/views/dialogs/create-mindr/index.html?${parameters}`;
    const details = await messenger.windows.create({
        height: 365,
        width: 500,
        url,
        type: 'popup',
        allowScriptsToClose: true
    });

    store.dispatch(openDialog(dialogId, dialogType, details));

    // 
    // 
    // 
    // 
};

const showTimespanPresetEditor = async timePreset => {
    const dialogType = 'mailmindr:time-preset-editor';
    const hasDialog = await bringDialogToFront(dialogType);

    if (hasDialog) {
        return;
    }

    const dialogId = createMailmindrId('mailmindr:dialog:time-preset-editor');

    const parameters = new URLSearchParams();
    parameters.set('dialogId', dialogId);
    if (timePreset) {
        parameters.set('preset', JSON.stringify(timePreset));
    }

    const height = 475;
    const width = 380;
    const url = `/views/dialogs/time-preset-editor/index.html?${parameters}`;
    const details = await messenger.windows.create({
        height,
        width,
        url,
        type: 'popup',
        state: 'normal',
        allowScriptsToClose: true
    });

    store.dispatch(openDialog(dialogId, dialogType, details));
};

const handleStartup = async () => {
    try {
        const storedState = await initializeStorage();
        const localState = storedState || initialState;

        const { storeState } = await getStorage();

        const saveState = async () => {
            const state = store.getState();

            await storeState(state);
        };
        const saveStateThrottled = throttle(saveState, 250);

        store = createStore(rootReducer, localState);
        store.addEventListener('change', saveStateThrottled);

        setupUI();
        startHeartbeat();
    } catch (startupError) {
        logger.error(`mailmindr failed to start`, startupError);
        // 
        const errorButtonCaption = browser.i18n.getMessage(
            'errorInitializationBrowserButtonText'
        );
        browser.browserAction.setTitle({ title: errorButtonCaption });
    }
};

const findExistingMindrForTab = async (tab, message) => {
    const { id, windowId } = tab;
    const msg = message || (await getCurrentDisplayedMessage({ id, windowId }));
    if (msg) {
        logger.log(`findExistingMindrForTab`, store);
        const { headerMessageId } = msg;
        const sanitizedHeaderMessageId = sanitizeHeaderMessageId(
            headerMessageId
        );
        const { mindrs } = store.getState();
        const mindr = mindrs.find(
            mindr =>
                sanitizeHeaderMessageId(mindr.headerMessageId) ===
                    sanitizedHeaderMessageId && !!sanitizedHeaderMessageId
        );

        return mindr;
    }

    return null;
};

const handleSelectedMessagesChanged = async (tab, messageList) => {
    const { id, windowId } = tab;
    const msg = await getCurrentDisplayedMessage({ id, windowId });
    if (!msg) {
        logger.log(`handleSelectedMessagesChanged: no message selected, exit.`);
        return;
    }

    const mindr = await findExistingMindrForTab(tab, msg);
    const title = !!mindr ? '!' : null;
    const details = { text: title };

    messenger.messageDisplayAction.setBadgeText(details);

    const tabInfo = await messenger.tabs.get(id);
    if (!tabInfo.mailTab && tabInfo.type !== 'messageDisplay') {
        logger.error(`this is not a mail tab`, tabInfo);
        return;
    }

    logger.log(
        `handleSelectedMessageChanged: ${tabInfo.mailTab}? -> ${tabInfo.url}`
    );

    if (!mindr) {
        logger.log(`There's no mindr present for this tab`, { tabInfo, msg });
        await messenger.tabs.executeScript(id, {
            code: `typeof removeExistingMessageBars === 'function' && removeExistingMessageBars()`
        });
        return;
    }

    await messenger.messageDisplayAction.setBadgeBackgroundColor({
        color: '#ad3bff'
    });
    await messenger.tabs.insertCSS(id, {
        file: '/styles/message-bar.css'
    });

    await messenger.tabs.executeScript(id, {
        file: '/scripts/mailmindr-message-script.js'
        // 
    });
    await messenger.tabs.executeScript(id, {
        // 
        code: `createMindrBar('${JSON.stringify(mindr.guid)}')`
    });
};

const getCurrentDisplayedMessage = async tab => {
    const correlationId = createCorrelationId('getCurrentDisplayedMessage');
    const { id: tabId, windowId = messenger.windows.WINDOW_ID_CURRENT } = tab;

    const message = await browser.messageDisplay.getDisplayedMessage(tabId);
    if (!message) {
        // 
        return null;
    }

    const messageId = message.id;

    logger.log(
        'BEGIN getCurrentDisplayedMessage, get full message and find messageHeaderId',
        { correlationId, messageId }
    );
    let messageWithHeader = null;
    try {
        messageWithHeader = await browser.messages.getFull(messageId);
    } catch (ex) {
        logger.warn(`could not retrieve full message`, {
            correlationId,
            messageId,
            error: ex,
            errorMessage: ex.message
        });
    }
    logger.log(
        'END getCurrentDisplayedMessage, get full message and find messageHeaderId',
        { correlationId, messageId }
    );
    if (messageWithHeader) {
        const headerMessageIds = messageWithHeader.headers['message-id'];
        logger.log(`headerMessageIds of selected Message`, {
            correlationId,
            headerMessageIds
        });

        let headerMessageId = null;
        try {
            headerMessageId = messageWithHeader.headers['message-id'][0];
        } catch (exception) {
            logger.error(`message with header`, {
                correlationId,
                messageWithHeader,
                headers: messageWithHeader.headers
            });
        }

        return {
            ...message,
            headerMessageId: sanitizeHeaderMessageId(headerMessageId)
        };
    }

    return message;
};

const refreshButtons = async mindrs => {
    try {
        const hasMindrs = mindrs.length;
        const current = await messenger.tabs.query({
            currentWindow: true,
            active: true
        });

        if (!current || !Array.isArray(current) || current.length === 0) {
            return;
        }

        const { id, windowId: _ } = current[0];

        messenger.browserAction.enable(id);

        // 
        // 
        messenger.messageDisplayAction.enable(id);

        // 
        handleSelectedMessagesChanged(current[0]);
    } catch (e) {
        logger.error(`refreshButtons: `, e);
    }
};

const onLoadDialog = dialogOpenInfo => {
    const { name, ...rest } = dialogOpenInfo;
    const state = store.getState();

    if (!store || !state) {
        logger.error(`State '(${state})' or store '(${store})' isn't defined`, {
            state,
            store
        });
    }
    switch (name) {
        case 'set-mindr': {
            const { guid } = rest;
            const settings = selectSettings(state);
            const presets = selectPresets(state);
            const mindr = selectMindrByGuid(state, guid);

            return {
                settings,
                presets,
                mindr
            };
        }
        case 'set-outgoing-mindr': {
            const settings = selectSettings(state);
            const presets = selectPresets(state);
            const {
                sender: { windowId, id }
            } = rest;
            const draft = selectDraftForSenderTabOrNull(state, {
                windowId,
                id
            });

            return {
                settings,
                presets,
                draft
            };
        }
        case 'mindr-alert': {
            // 
            const { active, overdue } = store.getState();
            return {
                active: active.filter(showReminderForMindr),
                overdue: overdue.filter(showReminderForMindr)
            };
        }
        case 'time-preset-editor': {
            logger.log('REST', rest);
            return { ...rest };
            break;
        }
        default:
            logger.log(
                `mailmindr:onLoadDialog // no data loadable for dialog '${name}'`
            );
    }
};

const onWindowRemoved = async windowId => {
    const log = logger.createContextLogger({ name: 'onWindowRemoved' });

    const state = store.getState();
    const openDialogs = selectOpenDialogs(state);
    const settings = selectSettings(state);
    const presets = selectPresets(state);

    const dialogInfo = openDialogs.find(
        dialog => dialog.details.id === windowId
    );
    logger.info(`onWindowRemoved called`, dialogInfo);
    if (dialogInfo) {
        logger.warn(
            `onWindowRemoved: will remove dialog: ${windowId} w/ ${dialogInfo.dialogId}`
        );
        const { dialogId, dialogType = '' } = dialogInfo;
        switch (dialogType.toLowerCase()) {
            case 'mailmindr:time-preset-editor':
                await sendConnectionMessage('connection:mailmindr-options', {
                    topic: 'settings:unlock',
                    message: { settings: { settings, presets } }
                });
                break;
        }
        store.dispatch(closeDialog(dialogId));
    }
};

const findIceboxFolderForAccountIdentityMailAddress = async identityMailAddress => {
    const { settings } = store.getState();
    const defaultIceboxFolder = await getGenericIceboxFolderForIdentityOrNull(
        identityMailAddress,
        settings
    );

    return defaultIceboxFolder;
};

const moveToIcebox = async (headerMessageId, metaData) => {
    const { folderAccountIdentityMailAddress } = metaData;

    const defaultIceboxFolder = await findIceboxFolderForAccountIdentityMailAddress(
        folderAccountIdentityMailAddress
    );

    if (!defaultIceboxFolder) {
        return false;
    }

    const correlationId = createCorrelationId('moveToIcebox');
    logger.log('BEGIN moveToIcebox', {
        correlationId,
        headerMessageId,
        metaData
    });

    const sourceFolder = {
        accountId: metaData.folderAccountId,
        path: metaData.folderPath
    };

    const targetFolder = await genericFolderToLocalFolder(defaultIceboxFolder);
    const { author, subject } = metaData;

    const messageDetails = {
        headerMessageId,
        author,
        subject
    };

    const success = await moveMessageToFolder(
        messageDetails,
        sourceFolder,
        targetFolder
    );

    logger.log('END moveToIcebox', {
        correlationId,
        headerMessageId,
        metaData
    });

    return success;
};

const messageHandler = async (request, _sender, _sendResponse) => {
    const { action, payload } = request;

    let result = null;

    switch (action) {
        case 'dialog:open':
            result = {
                status: 'ok',
                payload: onLoadDialog(payload)
            };

            return result;
        case 'dialog:close':
            {
                const dialogId = payload;
                const state = store.getState();
                const openDialogs = selectOpenDialogs(state);
                const dialog = openDialogs.find(
                    dialog => dialog.dialogId === dialogId
                );

                store.dispatch(closeDialog(dialogId));

                return { status: 'ok', dialog };
            }
            break;
        case 'dialog:bring-to-front':
            const { dialogType } = payload;
            const hasDialog = await bringDialogToFront(dialogType);
            if (hasDialog) {
                return { status: 'ok', paylad: null };
            } else {
                return { status: 'error', paylad: null };
            }
            break;
        case 'mindrs:list': {
            const state = store.getState();
            const mindrsList = selectMindrs(state);
            result = {
                status: 'ok',
                payload: {
                    mindrs: mindrsList
                }
            };
            return result;
        }
        case 'mindr:create-outgoing': {
            const correlationId = createCorrelationId(
                `messageHandler:mindr:create`
            );
            logger.log(`BEGIN messageHandler:mindr:create-outgoing`, {
                correlationId,
                payload
            });

            store.dispatch(createOrUpdateDraft(payload));

            const result = {
                status: 'ok',
                payload: {}
            };
            logger.log(`END messageHandler:mindr:create-outgoing`, {
                correlationId,
                payload
            });

            return result;
        }
        case 'mindr:create': {
            const { guid, headerMessageId, metaData, doMoveToIcebox } = payload;
            const correlationId = createCorrelationId(
                `messageHandler:mindr:create`
            );
            logger.log(`BEGIN messageHandler:mindr:create`, {
                correlationId,
                payload
            });
            try {
                logger.log('create templates', { correlationId });
                const createdMindr = await createMindrFromActionTemplate({
                    ...payload
                });

                logger.log('await close dialog', { correlationId });

                if (doMoveToIcebox) {
                    const isIceBoxed = Boolean(guid)
                        ? false
                        : await moveToIcebox(headerMessageId, metaData);

                    if (isIceBoxed) {
                        // 
                        // 
                        if (!createdMindr.action.moveMessageTo) {
                            const state = store.getState();
                            const existingMindr = selectMindrByGuid(
                                state,
                                guid
                            );
                            if (existingMindr) {
                                logger.log(
                                    `updating existing mindr, set icebox folder from original mindr`,
                                    {
                                        correlationId,
                                        source_moveMessageTo:
                                            existingMindr.action.moveMessageTo
                                    }
                                );
                                // 
                                // 
                                createdMindr.action.moveMessageTo =
                                    existingMindr.action.moveMessageTo;
                            } else {
                                // 
                                const destinationFolderDetails = {
                                    accountId: metaData.folderAccountId,
                                    name: metaData.folderName,
                                    path: metaData.folderPath,
                                    type: metaData.folderType,
                                    identityEmailAddress:
                                        metaData.folderAccountIdentityMailAddress
                                };

                                // 
                                const defaultIceboxFolder = await findIceboxFolderForAccountIdentityMailAddress(
                                    metaData.folderAccountIdentityMailAddress
                                );
                                const iceBoxFolder = await genericFolderToLocalFolder(
                                    defaultIceboxFolder
                                );
                                createdMindr.metaData.folderAccountId =
                                    iceBoxFolder.accountId;
                                createdMindr.metaData.folderName =
                                    iceBoxFolder.name;
                                createdMindr.metaData.folderPath =
                                    iceBoxFolder.path;
                                createdMindr.metaData.folderType =
                                    iceBoxFolder.type;
                                createdMindr.metaData.folderAccountIdentityMailAddress =
                                    iceBoxFolder.identityEmailAddress;
                                createdMindr.action.moveMessageTo = await localFolderToGenericFolder(
                                    destinationFolderDetails
                                );
                            }
                        }
                    }
                }

                store.dispatch(createOrUpdateMindr(createdMindr));

                result = {
                    status: createdMindr ? 'ok' : 'error',
                    payload: {
                        message: 'mindr creation failed'
                    }
                };

                logger.log('refresh buttons', { correlationId });
                const state = store.getState();

                await refreshButtons(selectMindrs(state));
                // 
            } catch (error) {
                logger.error(`FAIL messageHandler:mindr:create`, {
                    correlationId,
                    error,
                    payload
                });
            }

            logger.log(`END messageHandler:mindr:create`, {
                correlationId,
                payload
            });

            return result;
        }
        case 'mindr:remove-outgoing-mindr': {
            const { sender } = payload;
            const state = store.getState();
            const draft = selectDraftForSenderTabOrNull(state, sender);

            store.dispatch(removeDraft(draft));

            return { status: 'ok', paylad: null };
        }
        case 'mindr:remove': {
            const { guid } = payload;
            store.dispatch(removeMindr(guid));

            return { status: 'ok', paylad: null };
        }
        case 'mindr:get-information': {
            logger.log('mindr:get-information', payload);
            const { guid } = payload;

            const { mindrs } = store.getState();
            const mindr = mindrs.find(mindr => mindr.guid === guid);

            logger.log(`mindr:get-information: ${mindr.headerMessageId}`);

            return { status: 'ok', payload: { mindr } };
        }
        case 'show:time-preset-editor': {
            const { preset } = payload;
            showTimespanPresetEditor(preset);
            break;
        }
        case 'navigate:open-message-by-mindr-guid':
            {
                // 
                // 
                const { guid } = payload;
                const state = store.getState();
                const mindr = selectMindrByGuid(state, guid);

                if (mindr) {
                    const headerMessageId = sanitizeHeaderMessageId(
                        mindr.headerMessageId
                    );
                    if (messenger.messageDisplay.open) {
                        logger.log(
                            `Open message by headerMessageId [nativeAPI] < '${headerMessageId}' >`
                        );
                        try {
                            await messenger.messageDisplay.open({
                                headerMessageId,
                                active: true
                            });
                        } catch (openMessageError) {
                            logger.error(
                                `The message cannot be opened`,
                                openMessageError
                            );
                        }
                    }
                }
            }
            break;
        case 'refresh:buttons':
            await refreshButtons();
            break;
        case 'mindr:snooze': {
            const { minutes, list } = payload;
            const correlationId = createCorrelationId(
                `messageHandler:mindr:snooze`
            );

            const localState = store.getState();
            const mindrs = selectMindrs(localState);
            const presets = selectPresets(localState);
            const settings = selectSettings(localState);

            const { snoozeTime } = settings;

            logger.log(`mindr:snooze // snoozeTime: ${snoozeTime}`, {
                snoozeTime,
                mindrs
            });

            store.dispatch(
                snoozeMindrs(list, mindrs, snoozeTime, correlationId)
            );

            store.dispatch(heartBeat());

            const { active, overdue } = store.getState();
            const overdueNotExecuted = overdue.filter(item => !item.isExecuted);

            store.dispatch(
                showMindrAlert({ overdue: overdueNotExecuted, active })
            );

            return { status: 'ok', paylad: null };
        }
        case 'mindr:dismiss': {
            const { list } = payload;

            do {
                const guid = list.pop();

                store.dispatch(removeMindr(guid));
            } while (list.length);

            const { active, overdue } = store.getState();
            const overdueNotExecuted = overdue.filter(item => !item.isExecuted);

            store.dispatch(
                showMindrAlert({ overdue: overdueNotExecuted, active })
            );

            return { status: 'ok', paylad: null };
        }
        case 'preset:modify-timespan': {
            const { source, current } = payload;

            if (source) {
                // 
                logger.warn(`updating timespan`, current, source);
                store.dispatch(updateTimespanPreset(current, source));
            } else {
                // 
                logger.info(`creating new timespan`, current);
                store.dispatch(createTimespanPreset(current));
            }

            const state = store.getState();
            const settings = selectSettings(state);
            const presets = selectPresets(state);

            await sendConnectionMessage('connection:mailmindr-options', {
                topic: 'settings:unlock',
                message: { settings: { settings, presets } }
            });

            return { status: 'ok', paylad: null };
        }
        case 'preset:remove-timespans': {
            const { presets: currentPresets } = payload;

            if (
                currentPresets &&
                Array.isArray(currentPresets) &&
                currentPresets.length
            ) {
                store.dispatch(removeTimespanPreset(currentPresets));
                const state = store.getState();
                const settings = selectSettings(state);
                const presets = selectPresets(state);

                await sendConnectionMessage('connection:mailmindr-options', {
                    topic: 'settings:updated',
                    message: { settings: { settings, presets } }
                });
            }

            return { status: 'ok', paylad: null };
        }
        case 'options:get-settings': {
            const state = store.getState();
            const settings = selectSettings(state);
            const presets = selectPresets(state);

            await sendConnectionMessage('connection:mailmindr-options', {
                topic: 'settings:updated',
                message: { settings: { settings, presets } }
            });
            break;
        }
        case 'settings:set': {
            let { name, value } = payload;
            logger.info(`→ set '${name}' to '${value}'`, value);

            const state = store.getState();
            const settings = selectSettings(state);
            const propNames = Object.keys(settings);

            switch (name) {
                case 'setIceBoxFolder':
                    const { accountIdentity, folder } = value;
                    const genericFolder = await localFolderToGenericFolder(
                        folder
                    );
                    const newIceBoxFolder = {
                        accountIdentity,
                        folder: genericFolder
                    };
                    let iceBoxFolders = settings.iceboxFolders || [];
                    let indexOfIceBoxFolder = iceBoxFolders.findIndex(
                        folder => folder.accountIdentity === accountIdentity
                    );
                    if (indexOfIceBoxFolder === -1) {
                        iceBoxFolders = [...iceBoxFolders, newIceBoxFolder];
                    } else {
                        let copyOfIceBoxFolders = [...iceBoxFolders];
                        copyOfIceBoxFolders.splice(
                            indexOfIceBoxFolder,
                            1,
                            newIceBoxFolder
                        );
                        iceBoxFolders = copyOfIceBoxFolders;
                    }

                    // 
                    name = 'iceboxFolders';
                    value = iceBoxFolders;

                    break;
            }

            if (propNames.indexOf(name) >= 0) {
                store.dispatch(updateSetting(name, value));
                const state = store.getState();
                const settings = selectSettings(state);
                const presets = selectPresets(state);

                await sendConnectionMessage('connection:mailmindr-options', {
                    topic: 'settings:updated',
                    message: { settings: { settings, presets } }
                });
            } else {
                logger.error(
                    `Setting '${name}' not found. Is the default value missing?`
                );
            }
            break;
        }
        case 'settings:force-unlock': {
            const state = store.getState();
            const settings = selectSettings(state);
            const presets = selectPresets(state);
            await sendConnectionMessage('connection:mailmindr-options', {
                topic: 'settings:unlock',
                message: { settings: { settings, presets } }
            });
            break;
        }
        case 'do:mindr-action-edit': {
            const { guid } = payload;
            await editMindrByGuid(guid);
            break;
        }
        case 'do:mindr-action-remove': {
            const { guid } = payload;
            const state = store.getState();
            const mindr = selectMindrByGuid(state, guid);

            if (!mindr) {
                return;
            }

            const { due } = mindr;
            const relative = due.getTime() - Date.now();

            if (relative < 0) {
                // 
                await messageHandler({
                    action: 'mindr:dismiss',
                    payload: { list: [guid] }
                });
            } else {
                // 
                await messageHandler({
                    action: 'mindr:remove',
                    payload: { guid }
                });
            }

            break;
        }
        default:
            logger.error(
                'mailmindr:messageHandler // unhandled message received',
                request
            );
    }

    // 
    const localState = store.getState();
    const mindrs = selectMindrs(localState);

    if (mindrs.length) {
        messenger.browserAction.enable();
    } else {
        messenger.browserAction.disable();
    }

    return result;
};

const onConnectionMessage = async message => {
    logger.log('received message via post: ', message);
    return await messageHandler(message);
};

const connectionHandler = async port => {
    const {
        sender: { id },
        name
    } = port;

    if (id !== 'mailmindr@arndissler.net') {
        logger.error(
            `mailmindr doesn't support cross webextension communication (yet).`
        );
        // 
        return;
    }

    logger.log(`connectionHandler called for ${name}`);
    if (name === 'connection:mindr-alert') {
        port.onMessage.addListener(onConnectionMessage);
        port.onDisconnect.addListener(() =>
            store.dispatch(connectionClosed(port))
        );
        store.dispatch(connectionOpened(port));

        // 
        const { overdue, active } = store.getState();
        await sendConnectionMessage(name, {
            overdue: overdue.filter(showReminderForMindr),
            active: active.filter(showReminderForMindr)
        });
    } else if (name === 'connection:mailmindr-options') {
        port.onMessage.addListener(onConnectionMessage);
        port.onDisconnect.addListener(() =>
            store.dispatch(connectionClosed(port))
        );
        store.dispatch(connectionOpened(port));
    }
};

browser.tabs.onMoved.addListener(async ({}) => {});

browser.tabs.onActivated.addListener(async activeInfo => {
    if (!store) {
        logger.error(`tabs.onActivated:handler: store is undefined`);
        return;
    }

    const { tabId, windowId } = activeInfo || {};
    const { mindrs } = store.getState();
    // 
    await refreshButtons(mindrs);
});

browser.mailTabs.onSelectedMessagesChanged.addListener(
    handleSelectedMessagesChanged
);

browser.messageDisplayAction.onClicked.addListener(
    async ({ id, windowId, type }) => {
        const currentMessage = await getCurrentDisplayedMessage({
            id,
            windowId
        });
        const mindr = await findExistingMindrForTab(
            {
                id,
                windowId
            },
            currentMessage
        );

        await showCreateOrUpdateMindrDialog(currentMessage, mindr);
        // 
    }
);

browser.menus.onShown.addListener(info => {
    const /** @type {browser.menus.ContextType[]} */ contexts = [
            'message_list',
            'page'
        ];
    // 
    if (
        info &&
        info.contexts &&
        (info.contexts || []).find(targetContext =>
            contexts.includes(targetContext)
        )
    ) {
        const oneMessage =
            info.selectedMessages && info.selectedMessages.messages.length == 1;
        const firstMessage = oneMessage
            ? info.selectedMessages.messages[0]
            : null;
        const createProperties = {
            contexts,
            id: 'mailmindrMenuSetFollowUp',
            onclick: async () => await setMindrForCurrentMessage(firstMessage),
            title: browser.i18n.getMessage(
                'extension.context.menu.item.create-mindr'
            ),
            icons: {
                '16': 'images/mailmindr-flag.svg',
                '32': 'images/mailmindr-flag.svg'
            }
        };

        browser.menus.create(createProperties);
        browser.menus.update('mailmindrMenuSetFollowUp', {
            visible: oneMessage
        });
        browser.menus.refresh();
    }

    browser.menus.remove('mailmindrMenuSetFollowUp');
});

messenger.windows.onRemoved.addListener(onWindowRemoved);

browser.runtime.onMessage.addListener(
    // 
    messageHandler
);

// 
// 
// 
// 
// 
// 
// 
// 
// 

messenger.compose.onBeforeSend.addListener((tab, details) => {
    logger.log(`ONBEFORESEND -- `, { tab, details });
    return { cancel: false };
});

messenger.compose.onAfterSend.addListener(async (tab, sendInfo) => {
    const { messages, mode, error, headerMessageId } = sendInfo;

    const findHeaderMessageId = ({ messages, headerMessageId }) => {
        if (headerMessageId) {
            logger.log(`🧠 sent headerMessageId: ${headerMessageId}`);
            return headerMessageId;
        } else if (messages && messages.length) {
            const message = messages[0];
            return message.headerMessageId;
        } else {
            return null;
        }
    };

    const hdrMsgId = findHeaderMessageId({ messages, headerMessageId });
    if (hdrMsgId) {
        // 
        const maybeDraft = selectDraftForSenderTabOrNull(store.getState(), tab);
        if (maybeDraft && maybeDraft.mindr) {
            const { mindr } = maybeDraft;
            const {
                author = '',
                subject = '',
                folder = null
            } = sendInfo?.messages?.[0];
            const sourceFolder = await localFolderToGenericFolder(folder);
            const {
                accountId: folderAccountId,
                identityEmailAddress: folderAccountIdentityMailAddress,
                name: folderName,
                path: folderPath,
                type: folderType
            } = sourceFolder;
            const createdMindr = await createMindrFromActionTemplate({
                ...mindr,
                headerMessageId,
                metaData: {
                    headerMessageId,
                    author,
                    subject,
                    folderAccountId,
                    folderName,
                    folderPath,
                    folderType,
                    folderAccountIdentityMailAddress
                }
            });

            store.dispatch(createOrUpdateMindr(createdMindr));
        }
    }

    logger.log(`ONAFTERSEND -- `, { tab, sendInfo });
});

browser.runtime.onConnect.addListener(connectionHandler);

browser.commands.onCommand.addListener(async command => {
    switch (command) {
        case 'mailmindr_set_follow_up':
            await setMindrForCurrentMessage();

            break;
        case 'mailmindr_open_list':
            await messenger.browserAction.openPopup();
            break;
    }
});

const thunderbirdVersion = findThunderbirdVersion();
const handleNewMailReceived = async (
    /** @type {messenger.folders.MailFolder} */ folder,
    /** @type {messenger.messages.MessageList} */ messages
) => {
    const /** @type {(mindr: string) => Mindr} */ selectByHeaderMessageId = selectMindrByHeaderMessageId.bind(
            null,
            store.getState()
        );
    const /** @type { { messageHeaderId: string, replyToHeaderMessageId: string }[] } */ headerMessageIds = [];
    for await (let msg of getMessages(messages)) {
        const messageId = msg.id;
        const message = await messenger.messages.getFull(messageId);
        // 
        const { headers } = message;
        const messageHeaderId = msg.headerMessageId;
        const inReplyTo = headers?.['in-reply-to'];
        if (inReplyTo) {
            headerMessageIds.push(
                ...inReplyTo.map(item => ({
                    messageHeaderId,
                    replyToHeaderMessageId: sanitizeHeaderMessageId(item)
                }))
            );
        }
    }

    const mindrs = headerMessageIds
        .map(({ replyToHeaderMessageId, messageHeaderId }) => {
            const mindr = selectByHeaderMessageId(replyToHeaderMessageId);
            if (mindr) {
                return { mindr, messageHeaderId };
            } else return undefined;
        })
        .filter(Boolean);

    await Promise.all(
        mindrs.map(async ({ mindr, messageHeaderId }) => {
            await store.dispatch(setReplyReceived(mindr, messageHeaderId));
        })
    );
};

if (thunderbirdVersion <= 121) {
    browser.messages.onNewMailReceived.addListener(handleNewMailReceived);
} else {
    browser.messages.onNewMailReceived.addListener(
        handleNewMailReceived,
        false
    );
}

handleStartup();
