import {
    genericFolderToLocalFolder,
    getFlatFolderList
} from '../../core-utils.mjs.js';
import { createCorrelationId, createLogger } from '../../logger.mjs.js';
import {
    applyActionToMessageInFolder,
    doMoveMessageToFolder
} from '../../message-utils.mjs.js';
import {
    lockMindrForExecution,
    unlockMindr,
    createOrUpdateMindr
} from './actions.mjs.js';

const logger = createLogger('modules/store/actions/executeMindr');

export const executeMindr = mindr => async (dispatch, getState) => {
    const { headerMessageId, metaData, action, guid } = mindr;
    logger.log(`START executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
        guid,
        headerMessageId
    });

    dispatch(lockMindrForExecution(mindr));

    const {
        author,
        subject,
        folderAccountId,
        folderName,
        folderPath,
        folderType,
        folderAccountIdentityMailAddress
    } = metaData;
    const correlationId = createCorrelationId('executeMindr');
    const executionStart = Date.now();
    const { copyMessageTo, moveMessageTo } = action;

    const applyAction = async (messageId, action) => {
        const {
            flag,
            markUnread,
            showReminder,
            tagWithLabel,
            copyMessageTo,
            moveMessageTo
        } = action;
        const messageProps = {
            ...(flag && { flagged: true }),
            ...(markUnread && { read: false })
        };

        logger.info(`→ apply update to message ${messageId}`, messageProps);

        const timeout = new Promise(resolve => setTimeout(resolve, 1000));
        const updater = messenger.messages.update(messageId, messageProps);

        await Promise.all([updater, timeout]);
    };

    const destinationFolder = moveMessageTo
        ? await genericFolderToLocalFolder(moveMessageTo)
        : null;
    const possibleSourceFolder = await genericFolderToLocalFolder({
        accountId: folderAccountId,
        path: folderPath,
        identityEmailAddress: folderAccountIdentityMailAddress
    });
    const flatFolderList = await getFlatFolderList();
    const localFlatFolderList = await Promise.all(
        flatFolderList
            .filter(({ type }) => type === 'folder')
            .map(async ({ folder }) => await genericFolderToLocalFolder(folder))
    );
    const folders = possibleSourceFolder
        ? [
              possibleSourceFolder,
              ...localFlatFolderList.filter(
                  fldr =>
                      fldr.accountId !== possibleSourceFolder.accountId &&
                      fldr.path !== possibleSourceFolder.path
              )
          ]
        : localFlatFolderList;

    logger.log(`BEGIN execution of ${mindr.guid}`, {
        correlationId,
        guid
    });
    const startTime = performance.now();

    const targetFolders = folders;

    let hasError = false;
    let iterationCount = 0;

    logger.log(`BEGIN targetFolder iteration`, {
        guid,
        correlationId,
        targetFolderCount: (targetFolders || []).length,
        targetFolders
    });

    const applyActionToMessage = async (message, messageFolder) => {
        const { id } = message;

        logger.log(`BEGIN applyActionToMessage`);
        await applyAction(id, action);
        logger.log(
            `Do we have a destination folder? ${
                destinationFolder ? 'yes' : 'no'
            }`,
            destinationFolder
        );
        if (destinationFolder) {
            await doMoveMessageToFolder(
                message,
                destinationFolder,
                correlationId
            );
        }
        logger.log(`END applyActionToMessage`);
    };

    const applyActionToFirstMessageInFolders = async () => {
        for await (let folder of targetFolders) {
            logger.log(
                ` -- executeMindr: folder loop, apply action to message in folder '(${folder.name})'`,
                {
                    correlationId,
                    folder,
                    targetFolders
                }
            );

            try {
                const actionResult = await applyActionToMessageInFolder(
                    folder,
                    { headerMessageId, author },
                    applyActionToMessage,
                    true
                );
                const { done, value } = await actionResult.next();
                const success = Boolean(done && value && value.executed);
                if (success) {
                    return true;
                }
            } catch (ex) {
                logger.error('ERROR: execute mindr // mailmindr: >> !!', {
                    correlationId,
                    guid,
                    exception: ex
                });
                hasError = true;
            }
            iterationCount++;
        }
        return false;
    };

    logger.log(`BEFORE applying actions`, { correlationId });
    await applyActionToFirstMessageInFolders();
    logger.log(`END applying actions`, { correlationId });

    logger.log(`END targetFolder iteration`, {
        correlationId,
        guid,
        targetFolderCount: (targetFolders || []).length,
        targetFolders
    });

    const endTime = performance.now();
    logger.log(
        `mailmindr: execution finished in ${endTime - startTime}ms`,
        moveMessageTo
    );

    const executionEnd = Date.now();
    const executionDuration = (executionEnd - executionStart) / 1000;

    if (executionDuration > 3 * 60) {
        logger.error(
            `Execution of mindr '${guid}' took more than 180 seconds`,
            { guid, correlationId, executionDuration }
        );
    } else if (executionDuration > 60) {
        logger.warn(`Execution of mindr '${guid}' took more than 60 seconds`, {
            guid,
            correlationId,
            executionDuration
        });
    } else {
        logger.warn(
            `Execution of mindr '${guid}' took ${executionDuration} seconds`,
            { guid, correlationId, executionDuration }
        );
    }
    logger.log(`END execution of ${mindr.guid}`, { correlationId, guid });
    logger.log(`END executeMindr ${guid} w/ msgHdrId: '${headerMessageId}'`, {
        guid,
        headerMessageId
    });

    const modifiedMindr = structuredClone(mindr);
    modifiedMindr.isExecuted = true;

    dispatch(unlockMindr(modifiedMindr));

    if (!hasError) {
        dispatch(createOrUpdateMindr(modifiedMindr));
    }

    return !hasError;
};
