import json
import sys
import logging
from Queue import Queue
from threading import Thread

import time

from plesk_mail_migrator.core.dumps.queue_reader import QueueDumpReader
from plesk_mail_migrator.core.dumps.queue_writer import QueueDumpWriter
from plesk_mail_migrator.core.dumps.stream_reader import StreamDumpReader
from plesk_mail_migrator.core.dumps.stream_writer import StreamDumpWriter
from plesk_mail_migrator.core.entities.mail_account import MailAccount
from plesk_mail_migrator.core.entities.utils.imap_flags_utils import IMAPFlagsUtils
from plesk_mail_migrator.core.mail_migrator_logging import init_logging, enable_console_logging, configure_logging
from plesk_mail_migrator.core.progress.backup.backup_progress_empty import BackupProgressEmpty
from plesk_mail_migrator.core.progress.backup.backup_progress_file import BackupProgressFile
from plesk_mail_migrator.core.progress.restore.restore_progress_empty import RestoreProgressEmpty
from plesk_mail_migrator.core.progress.restore.restore_progress_file import RestoreProgressFile
from plesk_mail_migrator.providers.imap.provider import IMAPProvider
from plesk_mail_migrator.providers.imap.server_detect.autoconfig_db import download_autoconfig_db
from plesk_mail_migrator.providers.imap.server_detect.detect import create_detection_methods
from plesk_mail_migrator.providers.maildir.provider import MaildirProvider
from plesk_mail_migrator.providers.provider_factory import ProviderFactory
from plesk_mail_migrator.utils.cmd_args.plain_cmd_args_parser import PlainCmdArgsParser
from plesk_mail_migrator.utils.cmd_args.cmd_args_parser import CommandLineArgumentException
from plesk_mail_migrator.utils.cmd_args.prefixed_cmd_args_parser import PrefixedCmdArgsParser
from plesk_mail_migrator.utils.exceptions import MailMigratorException
from plesk_mail_migrator.utils.file_utils import open_wrap_not_exists
from plesk_mail_migrator.utils.stop_mark import StopMark

null_logging_handler = logging.NullHandler()
logging.getLogger('plesk_mail_migrator').addHandler(null_logging_handler)
logger = logging.getLogger(__name__)


DEFAULT_QUEUE_SIZE = 100


def main():
    init_logging()

    try:
        if len(sys.argv) > 1:
            command_name = sys.argv[1]
            argparser = PlainCmdArgsParser(sys.argv[2:])
            configure_logging(argparser)

            if command_name == 'backup':
                logger.info("START Backup")
                mail_account = create_mail_account(argparser)
                provider = create_backup_provider(argparser)
                exclude_message_ids = read_message_ids(argparser)
                if argparser.contains('progress-file'):
                    progress_file = argparser.get('progress-file')
                else:
                    progress_file = None
                stop_mark = create_stop_mark(argparser)
                argparser.check_unused_arguments()
                command_backup(mail_account, provider, exclude_message_ids, progress_file, stop_mark)
                logger.info("END Backup")
            elif command_name == 'restore':
                logger.info("START Restore")
                mail_account = create_mail_account(argparser)
                provider = create_restore_provider(argparser)
                stop_mark = create_stop_mark(argparser)
                argparser.check_unused_arguments()
                command_restore(mail_account, provider, stop_mark)
                logger.info("END Restore")
            elif command_name == 'list-message-ids':
                logger.info("START List message IDs")
                mail_account = create_mail_account(argparser)
                provider = create_restore_provider(argparser)
                filename = argparser.get('output-file')
                argparser.check_unused_arguments()
                command_list_message_ids(mail_account, provider, filename)
                logger.info("END List message IDs")
            elif command_name == 'migrate':
                logger.info("START Migrate")
                source_argparser = PrefixedCmdArgsParser(argparser, prefix="source")
                target_argparser = PrefixedCmdArgsParser(argparser, prefix="target")
                source_mail_account = create_mail_account(source_argparser, "Source mail account")
                target_mail_account = create_mail_account(target_argparser, "Target mail account")
                source_provider = create_backup_provider(source_argparser)
                target_provider = create_restore_provider(target_argparser)
                if argparser.contains('queue-size'):
                    queue_size = argparser.get('queue-size')
                else:
                    queue_size = DEFAULT_QUEUE_SIZE
                if argparser.contains('backup-progress-file'):
                    backup_progress_file = argparser.get('backup-progress-file')
                else:
                    backup_progress_file = None
                if argparser.contains('restore-progress-file'):
                    restore_progress_file = argparser.get('restore-progress-file')
                else:
                    restore_progress_file = None
                stop_mark = create_stop_mark(argparser)
                argparser.check_unused_arguments()
                command_migrate(
                    source_mail_account, source_provider, target_mail_account, target_provider,
                    queue_size, backup_progress_file, restore_progress_file, stop_mark
                )
                logger.info("END Migrate")
            elif command_name == 'print-dump-contents':
                logger.info("START Print dump contents")
                if not argparser.contains("dump-file"):
                    raise CommandLineArgumentException(
                        "Dump file is not specified. Specify it with --dump-file option."
                    )
                dump_file = argparser.get('dump-file')
                argparser.check_unused_arguments()
                command_print_dump_contents(dump_file)
                logger.info("END Print dump contents")
            elif command_name == 'download-autoconfig-db':
                enable_console_logging()
                logger.info("START Download autoconfig database")
                target_file = argparser.get('target-file')
                command_download_autoconfig_db(target_file)
                logger.info("END Download autoconfig database")
            elif command_name == 'detect-imap-host':
                enable_console_logging()
                logger.info("START Detect IMAP host")
                domain_name = argparser.get('domain-name')
                dns_server = argparser.get('dns-server')
                ca_bundle = argparser.get('ca-bundle')
                command_detect_imap_host(domain_name, dns_server, ca_bundle)
                logger.info("END Detect IMAP host")
            elif command_name in ['help', '--help', '/help', '-h', '-?', '/h', '/?']:
                logger.info("START Help")
                command_help()
                logger.info("END Help")
            else:
                raise CommandLineArgumentException(
                    "Invalid usage: invalid command was specified.\n"
                    "Allowed commands: backup, restore, list-message-ids, print-dump-contents, help."
                )
        else:
            raise CommandLineArgumentException("Invalid usage: no commands were specified")
    except CommandLineArgumentException as e:
        logger.debug("Exception: ", exc_info=True)
        logger.error("Command line arguments exception: %s", str(e))
        sys.stderr.write("%s\n" % (str(e),))
        sys.exit(1)
    except MailMigratorException as e:
        logger.debug("Exception: ", exc_info=True)
        logger.error(str(e))
        sys.stderr.write("%s\n" % (str(e),))
        sys.exit(1)
    except Exception as e:
        logger.debug("Exception: ", exc_info=True)
        logger.debug("Internal error of mail migrator: %s", str(e))
        sys.stderr.write("Internal error of mail migrator: %s\n" % (str(e),))
        sys.exit(1)


def command_backup(account, provider, exclude_message_ids, progress_file, stop_mark):
    """
    :type account: plesk_mail_migrator.core.entities.mail_account.MailAccount
    :type provider: plesk_mail_migrator.core.provider.backup_provider.BackupProvider
    :type progress_file: str | unicode | None
    :type exclude_message_ids: set[str]
    :type stop_mark: plesk_mail_migrator.utils.stop_mark.StopMark
    :rtype: None
    """
    dumper = StreamDumpWriter()
    if progress_file is not None:
        progress = BackupProgressFile(progress_file)
    else:
        progress = BackupProgressEmpty()
    errors = provider.do_backup_messages(account, dumper, exclude_message_ids, progress, stop_mark)
    if len(errors) > 0:
        raise MailMigratorException("\n\n".join(errors))


def command_restore(account, provider, stop_mark):
    """
    :type account: plesk_mail_migrator.core.entities.mail_account.MailAccount
    :type provider: plesk_mail_migrator.core.provider.restore_provider.RestoreProvider
    :type stop_mark: plesk_mail_migrator.utils.stop_mark.StopMark
    :rtype: None
    """
    dump_reader = StreamDumpReader()
    while True:
        if stop_mark.is_set():
            logger.debug("Restore was interrupted due to user request")
            return

        message = dump_reader.read_message()
        if message is None:
            break

        provider.do_restore_message(account, message)


def command_migrate(
    source_mail_account, source_provider, target_mail_account, target_provider, queue_size,
    backup_progress_file, restore_progress_file, stop_mark,
):
    """
    :type source_mail_account: plesk_mail_migrator.core.entities.mail_account.MailAccount
    :type source_provider: plesk_mail_migrator.core.provider.backup_provider.BackupProvider
    :type target_mail_account: plesk_mail_migrator.core.entities.mail_account.MailAccount
    :type target_provider: plesk_mail_migrator.core.provider.restore_provider.RestoreProvider
    :type queue_size: int
    :type backup_progress_file: str | unicode
    :type restore_progress_file: str | unicode
    :type stop_mark: plesk_mail_migrator.utils.stop_mark.StopMark
    :rtype: None
    """
    logger.debug("Queue size: %s", queue_size)
    queue = Queue(queue_size)
    dump_reader = QueueDumpReader(queue)
    dump_writer = QueueDumpWriter(queue)

    if backup_progress_file is not None:
        backup_progress = BackupProgressFile(backup_progress_file)
    else:
        backup_progress = BackupProgressEmpty()

    if restore_progress_file is not None:
        restore_progress = RestoreProgressFile(restore_progress_file)
    else:
        restore_progress = RestoreProgressEmpty()

    existing_message_ids = target_provider.list_message_ids(target_mail_account)
    errors = []

    def restore_thread_main():
        try:
            message_num = 1
            overall_messages_length = 0
            while True:
                if stop_mark.is_set():
                    logger.debug("Restore was interrupted due to user request")
                    return

                message = dump_reader.read_message()
                if message is None:
                    break
                try:
                    overall_messages_length += len(message.body)
                    restore_progress.set_restored_message(message_num)
                    restore_progress.set_stat_restored_messages(message_num, overall_messages_length)
                    target_provider.do_restore_message(target_mail_account, message)
                except Exception as e:
                    logger.debug("Exception: ", exc_info=True)
                    errors.append("Failed to restore mail message #%s: %s" % (message_num, str(e)))
                message_num += 1
        except Exception as e:
            logger.debug("Exception: ", exc_info=True)
            errors.append("Failed to restore mail messages: %s" % str(e))

    def backup_thread_main():
        try:
            errors.extend(
                source_provider.do_backup_messages(
                    source_mail_account, dump_writer, existing_message_ids, backup_progress, stop_mark
                )
            )
        except Exception as e:
            logger.debug("Exception: ", exc_info=True)
            errors.append("Failed to backup mail messages: %s" % str(e))
        finally:
            # put "None" marker meaning that there are no more messages
            queue.put(None)

    backup_thread = Thread(target=backup_thread_main, name='BackupThread')
    restore_thread = Thread(target=restore_thread_main, name='RestoreThread')
    backup_thread.start()
    restore_thread.start()
    backup_thread.join()
    restore_thread.join()

    if len(errors) > 0:
        raise MailMigratorException("\n\n".join(errors))


def command_list_message_ids(account, provider, filename):
    message_ids = provider.list_message_ids(account)
    with open(filename, 'w') as fp:
        fp.write("\n".join(message_ids))
        fp.write("\n")


def command_print_dump_contents(filename):
    not_exists_error_message = (
        "Dump file '{file}' with mail messages does not exist. Specify correct path to file."
    ).format(
        file=filename
    )
    with open_wrap_not_exists(filename, not_exists_error_message) as fp:
        dump_reader = StreamDumpReader(fp)

        first = True
        messages_count = 0

        while True:
            message = dump_reader.read_message()
            if message is None:
                break

            if not first:
                print
                print
            else:
                first = False

            print("=============MESSAGE BEGIN===================")
            print("Number: %s" % (messages_count + 1))
            print("Message ID: %s" % message.message_id)
            print("IMAP Folder: %s" % message.folder)
            print("IMAP Flags: %s" % IMAPFlagsUtils.flags_to_string(message.flags))
            print("Contents:")
            print("*********************************************")
            print(message.body)
            print("===============MESSAGE END===================")

            messages_count += 1

        if not first:
            print
            print

        print("Total messages: %s" % messages_count)


def command_download_autoconfig_db(target_filename):
    data = download_autoconfig_db()
    with open(target_filename, 'w') as fp:
        fp.write(json.dumps(data, sort_keys=True, indent=4))


def command_detect_imap_host(domain_name, dns_server, ca_bundle):
    for method in create_detection_methods(dns_server, ca_bundle):
        logger.info("Run method %s", method.get_method_id())
        start_time = time.time()
        try:
            result = method.detect(domain_name)
            logger.info("    Result: %s", ", ".join(result))
        except Exception as e:
            logger.info("    Detection failed: %s", e)
        end_time = time.time()
        logger.info("    Time elapsed: %.2f s", (end_time - start_time))


def command_help():
    help_message = """Tool used to migrate mail messages between mail accounts.
Usage:
mail-migrator <command> <arguments>

Available commands:
- backup - backup messages of mail account
- restore - restore messages of mail account
- list-message-ids - list message IDs of mail account
- print-dump-contents - print dump contents in human readable format
- help - display that help message

"backup" command
The command writes dump of account messages to standard output.
The dump has binary format.
Arguments:
--provider=<provider-id> - specify mail provider (mail server)
If no mail provider was specified, the tool will use IMAP mail provider.
--domain=<domain> - specify domain name of mail account
(everything after '@' symbol)
--mailname=<mailname> - specify mail name of mail account
(everything before '@' symbol)
--password=<password> - specify password of mail account
--account-description-file=<filename> - filename describing mail account to backup
File should contain domain name, mail name and password delimited by newlines.
Either accounts description file, or all domain name, mail name and password options should be specified.
Use the file to avoid password in command line.
--exclude-message-ids-file=<filename> - file with message ids that should not be dumped,
generated by list-message-ids command
--stop-mark-file=<filename> - existence of the specified file will be checked periodically,
and if the file exists then backup process will be stopped as soon as possible
--progress-file=<filename> - file to which backup progress will be written in JSON format.
The JSON will contain the following fields:
* 'error_id' - if error occurred during backup, this will contain ID of the error,
  which could be used for integration with other programs; possible values: 
  'connect', 'security', 'security-invalid-certificate', 'authentication', 'imap-host-autodetect'
* 'error_message' - if error occurred during backup, this will contain human readable error message 
* 'step' - ID of the step on which backup command works right now, could be used for integration with 
  other programs; possible values: 'connect', 'login', 'transfer-messages'

"restore" command
The command expects dump (generated by "backup" command) at standard input
Arguments:
--provider=<provider-id> - specify mail provider (mail server)
If no mail provider was specified, the tool will use mail directory provider.
--domain=<domain> - specify domain name of mail account
(everything after '@' symbol)
--mailname=<mailname> - specify mail name of mail account
(everything before '@' symbol)
--password=<password> - specify password of mail account
--account-description-file=<filename> - filename describing mail account to backup
File should contain domain name, mail name and password delimited by newlines.
Either accounts description file, or all domain name, mail name and password options should be specified.
--stop-mark-file=<filename> - existence of the specified file will be checked periodically,
and if the file exists then restore process will be stopped as soon as possible

"list-message-ids" command
List message IDs that are already exist on the server.
That command should be executed on the target server, and
its results should be provided to the source server
with --exclude-message-ids-file option
Arguments:
--provider=<provider-id> - specify mail provider (mail server)
If no mail provider was specified, the tool will use mail directory provider.
--domain=<domain> - specify domain name of mail account
(everything after '@' symbol)
--mailname=<mailname> - specify mail name of mail account
(everything before '@' symbol)
--password=<password> - specify password of mail account
--account-description-file=<filename> - filename describing mail account to backup
File should contain domain name, mail name and password delimited by newlines.
Either accounts description file, or all domain name, mail name and password options should be specified.
--output-file=<filename> - specify filename to write message IDs to

"migrate" command
Migrate mail messages from one mail server to another one.
The primary goal is to migrate to the server where the tool is running from another server, getting messages by IMAP.
Arguments:
--source-provider=<provider-id> - specify source mail provider (mail server)
IMAP is the only recommended option, it is selected by default.
Attention: If you specify any other mail provider than IMAP,
data will be always taken from the local server, that is not what you usually expect.
For IMAP provider specify host and port with --host and --port options.
--source-domain=<domain> - specify domain name of mail account
(everything after '@' symbol) on the source server
--source-mailname=<mailname> - specify mail name of mail account
(everything before '@' symbol) on the source server
--source-password=<password> - specify password of mail account on the source server
--source-account-description-file=<filename> - filename describing source mail account to migrate messages from
File should contain domain name, mail name and password delimited by newlines.
Either accounts description file, or all domain name, mail name and password options
should be specified for the source server.
--target-provider=<provider-id> - specify target mail provider (mail server).
If not specified, mail migrator will use mail directory provider.
--target-domain=<domain> - specify domain name of mail account
(everything after '@' symbol) on the target server
--target-mailname=<mailname> - specify mail name of mail account
(everything before '@' symbol) on the target server
--target-password=<password> - specify password of mail account on the target server
--target-account-description-file=<filename> - filename describing target mail account to migrate messages to
File should contain domain name, mail name and password delimited by newlines.
Either accounts description file, or all domain name, mail name and password options
should be specified for the target server.
--queue-size=<queue-size> - size of migration queue - maximum number of migrated messages that are stored in memory.
Increasing that parameter may improve speed of migration, but will require more memory.
--backup-progress-file=<filename> - file to which backup progress will be written in JSON format.
The JSON will contain the following fields:
* 'error_id' - if error occurred during backup, this will contain ID of the error,
  which could be used for integration with other programs; possible values: 
  'connect', 'security', 'security-invalid-certificate', 'authentication', 'imap-host-autodetect'
* 'error_message' - if error occurred during backup, this will contain human readable error message 
* 'step' - ID of the step on which backup command works right now, could be used for integration with 
  other programs; possible values: 'connect', 'login', 'transfer-messages'
--restore-progress-file=<filename> - file to which restore progress will be written in JSON format.
The JSON will contain the following field:
* 'restored_message_num' - number of message (among all migrated messages) which is restored right now.
--stop-mark-file=<filename> - existence of the specified file will be checked periodically,
and if the file exists then migration process will be stopped as soon as possible

Provider-specific arguments for "migrate" command should be prefixed by "--source-" or "--target-" strings,
for source and target servers accordingly. For example, if provider has option "host", then
to specify source host "example.com" you should specify that option as "--source-host=example.com"

"print-dump-contents" command
Use this command for debugging, when you want to view
contents of binary dump generated by "backup" command.
Arguments:
--dump-file=<filename> - specify name of a file with dump
generated by "backup" command

"download-autoconfig-db" command
Use this command to download/update file which contains Mozilla/Thunderbird IMAP host
automatic configuration database. The database could be used by other commands
to avoid asking customer for IMAP server and automatic detection of the server.
File used by mail migrator commands is located at 'providers/imap/server_detect/autoconfig-db.json'
Arguments:
--target-file=<filename> - specify name of a file to put results to
in JSON format.

"detect-imap-host" command
Use this command to check how IMAP host is automatically detected by migrator.
It prints different ways of IMAP host detection with results and timings.
Arguments:
--domain-name=<domain> - specify name of domain to autodetect IMAP host for
--dns-server=<hostname-or-ip> - specify hostname or IP of a DNS server to retrieve DNS records
--ca-bundle=<path> - specify path to CA bundle for HTTPS methods to work correctly

Overall arguments, applicable to all commands:
--log-file=<filename> - specify name of a file to write log with debugging information to"""
    print(help_message)

    print('')
    print("Available providers for backup:")
    for provider in ProviderFactory.get_backup_providers():
        print('- %s (%s)' % (provider.get_provider_id(), provider.get_title()))

    print('')
    print("Available providers for restore:")
    for provider in ProviderFactory.get_restore_providers():
        print('- %s (%s)' % (provider.get_provider_id(), provider.get_title()))

    print('')
    print('Additional arguments that could be passed for specific providers')
    print('(add "source" or "target" prefix in case of "migrate" command):')

    for provider in ProviderFactory.get_all_providers():
        parameters = provider.get_parameters()
        if len(parameters) > 0:
            print("Provider '%s'" % provider.get_provider_id())
            for parameter in parameters:
                print("--%s=<%s>: %s" % (parameter.name, parameter.help_value, parameter.description))


def create_mail_account(argparser, log_mail_account_title=None):
    """Get information about account to backup/restore/migrate

    :type log_mail_account_title: str | None
    :type argparser: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CmdArgsParser
    :rtype: plesk_mail_migrator.core.entities.mail_account.MailAccount
    """
    if log_mail_account_title is None:
        log_mail_account_title = "Mail account"

    if argparser.contains('account-description-file'):
        description_file_name = argparser.get('account-description-file')
        not_exists_error_message = (
            "File '{file}' with account description does not exist. Specify correct path to file."
        ).format(
            file=description_file_name
        )
        with open_wrap_not_exists(description_file_name, not_exists_error_message) as fp:
            description_file_contents = fp.read()
        description_file_lines = description_file_contents.split("\n")

        if len(description_file_lines) != 3:
            raise CommandLineArgumentException(
                "Account description file '{file}' has invalid format. "
                "It should have 3 lines, while it actually has {actual_lines_count} lines. "
                "The first line should contain domain name, the second line should contain mailname and "
                "the third line should contain password.".format(
                    file=description_file_name,
                    actual_lines_count=len(description_file_lines)
                )
            )

        domain_name = description_file_lines[0].strip()
        mailname = description_file_lines[1].strip()
        password = description_file_lines[2].strip()
    else:
        if (
            not argparser.contains('domain') or
            not argparser.contains('mailname') or
            not argparser.contains('password')
        ):
            raise CommandLineArgumentException(
                "Information about account was not specified. Please specify it by providing "
                "{domain_param_name}, {mailname_param_name} and {password_param_name} arguments "
                "(all of them must be specified). "
                "Alternatively you could provide this information with file using "
                "{account_description_file_param_name} option. The file should contain domain name, "
                "mailname and password delimited by newline character. "
                "Use the file to avoid plain text password in command line. ".format(
                    domain_param_name=argparser.get_full_param_name('domain'),
                    mailname_param_name=argparser.get_full_param_name('mailname'),
                    password_param_name=argparser.get_full_param_name('password'),
                    account_description_file_param_name=argparser.get_full_param_name('account-description-file')
                )
            )
        else:
            domain_name = argparser.get('domain')
            mailname = argparser.get('mailname')
            password = argparser.get('password')

    logger.info("%s: mailname=%s, domain=%s", log_mail_account_title, mailname, domain_name)
    return MailAccount(mailname, password, domain_name)


def create_backup_provider(argparser):
    """Create backup provider, according to provided command-line options

    :type argparser: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CmdArgsParser
    """
    providers = {
        'imap': IMAPProvider()
    }

    default_provider_id = 'imap'

    if argparser.contains('provider'):
        provider_id = argparser.get('provider')
    else:
        provider_id = default_provider_id

    if provider_id not in providers:
        raise CommandLineArgumentException(
            "Invalid backup provider was specified by '{option}' option: {specified}. "
            "Allowed values are: {allowed}".format(
                option=argparser.get_full_param_name('provider'),
                specified=provider_id, allowed=", ".join(providers.keys())
            )
        )
    provider = providers[provider_id]

    _fill_provider_parameters(provider, argparser)
    logger.info("Backup provider: %s", provider.get_title())
    for parameter in provider.get_parameters():
        logger.info("Backup provider parameter: %s=%s", parameter.name, parameter.value)

    return provider


def create_restore_provider(argparser):
    """Create restore provider, according to provided command-line options

    :type argparser: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CmdArgsParser
    """
    providers = {
        'maildir': MaildirProvider()
    }
    default_provider_id = 'maildir'

    if argparser.contains('provider'):
        provider_id = argparser.get('provider')
    else:
        provider_id = default_provider_id

    if provider_id not in providers:
        raise CommandLineArgumentException(
            "Invalid restore provider was specified by '{option}' option: {specified}. "
            "Allowed values are: {allowed}".format(
                option=argparser.get_full_param_name('provider'),
                specified=provider_id, allowed=", ".join(providers.keys())
            )
        )
    provider = providers[provider_id]

    _fill_provider_parameters(provider, argparser)
    logger.info("Restore provider: %s", provider.get_title())
    for parameter in provider.get_parameters():
        logger.info("Restore provider parameter: %s=%s", parameter.name, parameter.value)
    return provider


def create_stop_mark(argparser):
    """Create stop mark object, according to provided command-line options

    :type argparser: plesk_mail_migrator.utils.cmd_args.cmd_args_parser.CmdArgsParser
    :rtype: plesk_mail_migrator.utils.stop_mark.StopMark
    """
    if argparser.contains('stop-mark-file'):
        stop_mark_file = argparser.get('stop-mark-file')
    else:
        stop_mark_file = None

    return StopMark(stop_mark_file)


def _fill_provider_parameters(provider, argparser):
    """Fill provider parameters from command-line

    :type provider: plesk_mail_migrator.core.provider.base_provider.BaseProvider
    :type argparser: plesk_mail_migrator.utils.cmd_args_parser.CmdArgsParser
    """
    for parameter in provider.get_parameters():
        parameter.parse(argparser)


def read_message_ids(argparser):
    """Read message IDs to exclude from migration from file specified as command line argument.

    :type argparser: plesk_mail_migrator.utils.cmd_args_parser.CmdArgsParser
    :rtype: set[str]
    """
    message_ids = set()

    if argparser.contains("exclude-message-ids-file"):
        message_ids_file = argparser.get("exclude-message-ids-file")
        not_exists_error_message = (
            "File '{file}' with excluded messages IDs does not exist. Specify correct path to file."
        ).format(
            file=message_ids_file
        )
        with open_wrap_not_exists(message_ids_file, not_exists_error_message) as fp:
            for line in fp.readlines():
                line = line.strip()
                if line != '':
                    message_ids.add(line)
        logger.info("Exclude message IDs file '%s' contains %s IDs", message_ids_file, len(message_ids))

    return message_ids


if __name__ == '__main__':
    main()
