from parallels.core.logging import get_logger
from parallels.core import messages
import shutil
import string

from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.dump import dump
from parallels.core.registry import Registry

logger = get_logger(__name__)


class CreateConverted(CommonAction):
    _prefix_id = 0

    def __init__(self, subscription_backup):
        """Class constructor

        Arguments:
        - subscription_backup - object that can retrieve subscription backup
        object for each subscription, list of servers with backups
        """
        self._subscription_backup = subscription_backup

    def get_description(self):
        return messages.ACTION_CREATE_CONVERTED_BACKUPS

    def get_failure_message(self, global_context):
        return messages.FAILED_CREATE_CONVERTED_BACKUPS

    def get_logging_properties(self):
        return LoggingProperties(info_log=False)

    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        backup_ids = self._subscription_backup.get_backup_ids(global_context)
        for backup_id in backup_ids:
            cache_migration_list = global_context.session_files.get_converted_dump_migration_list_filename(backup_id)
            with global_context.migrator_server.runner() as runner:
                runner.remove_file(cache_migration_list)

            src_filename = self._make_cut_backup(global_context, backup_id)
            converted_filename = global_context.session_files.get_converted_dump_filename(backup_id)
            # clone source backup file to converted backup file
            shutil.copy2(src_filename, converted_filename)

    def _make_cut_backup(self, global_context, backup_id):
        """Make the cut backup, according to the current migration list.
        If there is no migration list - return the path of uncut backup.
        Otherwise, make a copy of uncut backup, cut that copy and return the path to the cut copy.

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        raw_filename = self._get_raw_backup_filename(backup_id)
        if global_context.migration_list_data is None:
            return raw_filename
        else:
            # 1. make a copy of the uncut backup
            cut_filename = global_context.session_files.get_path_to_cut_plesk_backup(backup_id)
            shutil.copy2(raw_filename, cut_filename)

            # 2. extract the relevant logins and subscriptions from migration list
            relevant_logins = (
                list(global_context.migration_list_data.resellers) +
                list(global_context.migration_list_data.customers_mapping.keys())
            )
            relevant_subscriptions = global_context.migration_list_data.subscriptions_mapping.keys()

            def load_unfiltered_backup(backup_filename):
                return dump.load(
                    backup_filename,
                    None,
                    # XXX expand-specific properties in common action
                    is_expand_mode=global_context.migrator.is_expand_mode(),
                    discard_mailsystem=global_context.migrator._is_mail_centralized(backup_id)
                )

            backup = load_unfiltered_backup(cut_filename)

            # certain subscription listed in migration list may belong to a client and/or reseller
            # not listed in that migration list,
            # and due to backup hierarchy, to reach this subscription we must leave this client and reseller in backup
            # 3. extract the relevant logins from backup, based on what other relevant
            # logins and subscriptions are under them.
            local_relevant_logins = []
            for sub, client, reseller in backup.iter_all_subscriptions_with_owner_and_reseller():
                if sub.name in relevant_subscriptions:
                    if client is not None:
                        local_relevant_logins.append(client.login)
                    if reseller is not None:
                        local_relevant_logins.append(reseller.login)

            for client, reseller in backup.iter_all_clients_with_owner():
                if client.login in relevant_logins:
                    if reseller is not None:
                        local_relevant_logins.append(reseller.login)

            # 4. cut from backup copy the subscriptions, clients and resellers not relevant to migration list
            backup.cut_irrelevant_subcriptions(relevant_subscriptions)
            backup.cut_irrelevant_clients(relevant_logins + local_relevant_logins)
            backup.cut_irrelevant_resellers(relevant_logins + local_relevant_logins)

            # 5. save modified backup copy
            backup.save_to_the_same_file(
                # set unique prefix for each migrated backup to avoid issues when
                # working with backups created at the same time when restoring them
                # with PMM
                prefix='migr%s_info' % (string.ascii_lowercase[CreateConverted._prefix_id])
            )
            CreateConverted._prefix_id += 1

            return cut_filename

    @staticmethod
    def _get_raw_backup_filename(backup_id):
        return Registry.get_instance().get_context().session_files.get_raw_dump_filename(backup_id)
