import {
    buildQueryInfoForMessageAndFolder,
    getMailAddress,
    sanitizeHeaderMessageId
} from './core-utils.mjs.js';
import { createLogger, createCorrelationId } from './logger.mjs.js';

const logger = createLogger('modules/message-utils');

export const getMessages = async function*(
    /** @type {messenger.messages.MessageList} */ list
) {
    let page = list;
    for (let message of page.messages) {
        yield message;
    }

    while (page.id) {
        page = await messenger.messages.continueList(page.id);
        for (let message of page.messages) {
            yield message;
        }
    }
};

const findMessage = async (
    messages,
    { author, headerMessageId },
    parentCorrelationId
) => {
    const correlationId = createCorrelationId(
        'findMessage',
        parentCorrelationId
    );
    const authorMailAddress = getMailAddress(author);
    logger.log(`BEGIN findMessage in ${messages.length} messages`, {
        correlationId,
        messages,
        author,
        authorMailAddress,
        headerMessageId
    });
    const maybeMatching = messages.filter(
        msg => getMailAddress(msg.author) === authorMailAddress
    );

    if (maybeMatching.length === 0) {
        logger.info(`no messages matching in this chunk`);
        return null;
    }

    logger.log(`matching messages: ${maybeMatching.length}`, {
        correlationId,
        maybeMatching
    });

    do {
        const message = maybeMatching.pop();
        const { id, headerMessageId: msgHdrId } = message;
        try {
            if (msgHdrId) {
                if (msgHdrId === headerMessageId) {
                    logger.log(
                        `SUCCESS headerMessageId found: '<${headerMessageId}>' === '${msgHdrId}'`
                    );
                    return message;
                } else {
                    // 
                    // 
                    // 
                }
            }

            const msgWithHeader = await browser.messages.getFull(id);
            const msgHdrId0 = sanitizeHeaderMessageId(
                msgWithHeader.headers['message-id'][0]
            );

            if (msgHdrId0 === sanitizeHeaderMessageId(headerMessageId)) {
                logger.log(
                    `SUCCESS headerMessageId found: '${headerMessageId}' === '${msgHdrId0}'`
                );
                return message;
            } else {
                // 
                // 
                // 
            }
        } catch (ex) {
            logger.warn(`could not retrieve full message`, {
                correlationId,
                messageId: id,
                error: ex,
                errorMessage: ex.message
            });
        }
    } while (maybeMatching.length > 0);
    logger.log(`END findMessage in ${messages.length} messages`, {
        correlationId,
        messages,
        author,
        authorMailAddress,
        headerMessageId
    });

    return null;
};

export async function* applyActionToMessageInFolder(
    folder,
    msg,
    action,
    returnOnFirstMessage
) {
    const correlationId = createCorrelationId('applyActionToMessagesInFolder');
    const { headerMessageId, author } = msg;
    const queryInfo = buildQueryInfoForMessageAndFolder(
        { headerMessageId, author },
        folder
    );
    const { path } = folder;

    logger.info(`BEGIN messages.query on folder '${path}'`, {
        correlationId,
        queryInfo
    });

    let queryResult = null;
    try {
        queryResult = await messenger.messages.query(queryInfo);
    } catch (queryError) {
        // 
        queryResult = null;
        logger.error(`applyActionToMessageInFolder: initial query failed`, {
            queryError,
            queryInfo
        });

        throw queryError;

        return Promise.resolve(null);
    }

    logger.log(`messages query done, result is`, {
        correlationId,
        queryResult
    });
    logger.info(`END messages.query on folder '${path}'`, {
        correlationId,
        queryInfo
    });

    if (
        queryResult.id === null &&
        Array.isArray(queryResult.messages) &&
        queryResult.messages.length === 0
    ) {
        logger.warn(
            `query result is { id:  null, messages: [] }, fallback to list on folder '${path}'`,
            { correlationId, queryResult }
        );

        queryResult = await messenger.messages.list(folder);
    }

    let continueIteration = true;
    let pageCount = 0;
    do {
        const message = await findMessage(
            queryResult.messages,
            msg,
            correlationId
        );
        if (message) {
            if (returnOnFirstMessage) {
                await action(message, folder);
                return Promise.resolve({ message, folder, executed: true });
            }

            await action(message, folder);
            yield Promise.resolve({ message, folder, executed: true });
        }

        if (queryResult.id !== null) {
            logger.log(`query next page ${pageCount}`, { correlationId });
            queryResult = await messenger.messages.continueList(queryResult.id);
            pageCount++;
        } else {
            continueIteration = false;
        }
    } while (continueIteration); // queryResult.id !== null); // && continueIteration);

    return Promise.resolve(null);
}

export const doMoveMessageToFolder = async (
    message,
    destinationFolder,
    parentCorrelationId
) => {
    const correlationId = createCorrelationId(
        'doMoveMessageToFolder',
        parentCorrelationId
    );
    try {
        const { id } = message;
        const { accountId, path } = destinationFolder;
        logger.log(`BEGIN move message ${id}`, {
            correlationId,
            message
        });
        await messenger.messages.move([id], { accountId, path });
        logger.log(`END move message ${id}`, {
            correlationId,
            message
        });
        return true;
    } catch (error) {
        logger.error(error, { correlationId });
        return false;
    }
};

export const moveMessageToFolder = async (
    messageDetails,
    sourceFolder,
    targetFolder
) => {
    const correlationId = createCorrelationId('moveMessagesToFolder');
    try {
        logger.info('moveMessageToFolder target', {
            correlationId,
            targetFolder
        });

        const moveMessageToFolderAction = async (
            message,
            _messageSourceFolder
        ) => {
            await doMoveMessageToFolder(message, targetFolder, correlationId);
        };

        logger.info(`BEGIN applying action to folder`, {
            correlationId,
            sourceFolder,
            messageDetails
        });
        const actionResult = await applyActionToMessageInFolder(
            sourceFolder,
            messageDetails,
            moveMessageToFolderAction,
            true
        );
        const { done, value } = await actionResult.next();

        // 
        const success = Boolean(done);

        const logMessage = `moveMessageToFolder : applyActionToMessageInFolder returns { done: ${done}, value: ${value} }`;
        if (success) {
            logger.info(logMessage, { correlationId, result: { done, value } });
        } else {
            logger.warn(logMessage, { correlationId, result: { done, value } });
        }

        if (value === null) {
            logger.error(
                `moveMessageToFolder : applyActionToMessageInFolder message not found in folder`
            );
        }

        logger.info(`END applying action to folder`, {
            correlationId,
            sourceFolder,
            messageDetails
        });
    } catch (e) {
        logger.error(`moveMessageToFolder failed: ${e.message}`, {
            correlationId,
            error: e
        });
        return false;
    }

    return true;
};
