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.logging import get_logger
from parallels.core.utils import migrator_utils
from parallels.core.utils.common import safe_string_repr
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
        """

        web_files = global_context.migrator.web_files
        """:type: parallels.core.utils.paths.web_files.BaseWebFiles"""

        source_server = subscription.web_source_server
        target_server = subscription.web_target_server
        items_to_copy = web_files.list_files_to_copy(global_context, subscription)
        logger.debug(messages.DEBUG_COPY_WEB_FILES, items_to_copy)

        for item in items_to_copy:
            global_context.safe.try_subscription_with_rerun(
                func=lambda: self._copy_content_item(global_context, subscription, item),
                name=subscription.name,
                error_message=safe_format(
                    messages.FAILED_TO_COPY_WEB_ITEM,
                    source_server=source_server.description(),
                    target_server=target_server.description(),
                    source_path=UnixSourceWebPathConverter().expand(item.source_path, source_server)
                ),
                is_critical=False,
                repeat_error=safe_format(
                    messages.FAILED_TO_COPY_WEB_ITEM_RETRY,
                    source_server=source_server.description(),
                    target_server=target_server.description(),
                    source_path=UnixSourceWebPathConverter().expand(item.source_path, source_server)
                )
            )

    @staticmethod
    def _copy_content_item(global_context, subscription, item):
        try:
            source_server = subscription.web_source_server
            target_server = subscription.web_target_server

            target_sysuser = subscription.converted_dump.phosting_sysuser_name
            rsync_additional_args = migrator_config.read_rsync_additional_args(global_context.config)

            if item.source_system_user_uid is not None and item.source_system_user_gid is not None:
                ssh_access_info = global_context.user_ssh_access_pool.get_by_uid_gid(
                    source_server, target_server,
                    item.source_system_user_uid, item.source_system_user_gid, target_sysuser
                )
            elif item.source_system_user_login is not None:
                ssh_access_info = global_context.user_ssh_access_pool.get_by_login(
                    source_server, target_server, item.source_system_user_login, target_sysuser
                )
            else:
                ssh_access_info = global_context.user_ssh_access_pool.get_by_login(
                    source_server, target_server, subscription.raw_dump.phosting_sysuser_name, target_sysuser
                )

            migrator_utils.copy_directory_content_unix(
                source_server.ip(),
                ssh_access_info.username,
                source_server,
                target_server,
                UnixSourceWebPathConverter().expand(item.source_path, source_server),
                UnixTargetWebPathConverter().expand(item.target_path, target_server),
                ssh_access_info.ssh_private_key_filename,
                item.exclude,
                item.skip_if_source_not_exists,
                rsync_additional_args=rsync_additional_args,
                source_rsync_bin=source_server.rsync_bin,
                source_port=source_server.settings().ssh_auth.port,
                target_user=target_sysuser
            )
        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)
