from parallels.core import MigrationError
from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core import migrator_config
from parallels.core import safe_format
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.actions.utils.multithreading_properties import MultithreadingProperties
from parallels.core.utils import migrator_utils
from parallels.core.utils.common import safe_string_repr
from parallels.core.utils.hosting_analyser_utils import apply_hosting_analyser_strategy
from parallels.core.utils.migrator_utils import SourceDirectoryDoesNotExistError
from parallels.core.utils.paths.converters.unix.source import UnixSourceWebPathConverter
from parallels.core.utils.paths.converters.unix.target import UnixTargetWebPathConverter

logger = get_logger(__name__)


class CopyUnixWebContent(SubscriptionAction):
    """Copy web files for Unix servers"""

    def get_description(self):
        """
        :rtype: str | unicode
        """
        return messages.COPY_WEB_FILES_FROM_UNIX_SERVERS

    def get_failure_message(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return safe_format(messages.FAILED_COPY_WEB_FILES_FROM_UNIX_SERVERS, subscription_name=subscription.name)

    def is_critical(self):
        """If action is critical or not

        If action is critical and it failed for a subscription, migration tool
        won't run the next operations for the subscription.

        :rtype: bool
        """
        return False

    def get_multithreading_properties(self):
        """Get how multithreading should be applied for that action

        :rtype: parallels.core.actions.utils.multithreading_properties.MultithreadingProperties
        """
        return MultithreadingProperties(can_use_threads=True, use_threads_by_default=True)

    def filter_subscription(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        # skip subscriptions w/o physical server and windows subscriptions
        source_info = subscription.get_source_info()
        if not source_info.is_server:
            return False
        if source_info.is_windows:
            return False

        return global_context.migrator.web_files.need_to_copy_files(global_context, subscription)

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        source_server = subscription.web_source_server
        target_server = subscription.web_target_server

        user_mapping = {}
        group_mapping = {}

        if source_server.apache_user is not None:
            if source_server.apache_user != target_server.apache_user:
                user_mapping[source_server.apache_user] = target_server.apache_user
            if source_server.apache_group != target_server.apache_group:
                group_mapping[source_server.apache_group] = target_server.apache_group

        tocopy = global_context.migrator.web_files.list_files_to_copy(global_context, subscription)
        logger.debug(messages.DEBUG_COPY_WEB_FILES, tocopy)
        rsync_additional_args = migrator_config.read_rsync_additional_args(global_context.config)
        apply_hosting_analyser_strategy(global_context, subscription, rsync_additional_args)

        key_info = global_context.ssh_key_pool.get(source_server, target_server)
        try:
            for item in tocopy:
                try:
                    migrator_utils.copy_directory_content_unix(
                        source_server.ip(),
                        source_server.user(),
                        source_server,
                        target_server,
                        UnixSourceWebPathConverter().expand(item.source_path, source_server),
                        UnixTargetWebPathConverter().expand(item.target_path, target_server),
                        key_info.key_pathname,
                        item.exclude,
                        item.skip_if_source_not_exists,
                        rsync_additional_args=rsync_additional_args,
                        user_mapping=user_mapping,
                        group_mapping=group_mapping,
                        source_rsync_bin=source_server.rsync_bin,
                        source_port=source_server.settings().ssh_auth.port,
                        preserve_owner=True,
                        preserve_group=True,
                        preserve_perms=True
                    )
                except SourceDirectoryDoesNotExistError as e:
                    # If directory does not exist on the source server - add error message
                    # but proceed to the next directories
                    global_context.safe.fail_subscription(subscription.name, safe_string_repr(e), is_critical=False)

        except Exception as e:
            logger.exception()
            raise MigrationError(
                safe_format(
                    messages.RSYNC_FAILED_COPY_FILES_FROM_SOURCE,
                    source=source_server.description(),
                    target=target_server.description(),
                    reason=e,
                )
            )
