import ntpath

from parallels.core.logging import get_logger
from parallels.core.runners.base import BaseRunner
from parallels.core.utils.paths.converters.windows.target import WindowsTargetWebPathConverter
from parallels.core.utils.paths import web_paths
from parallels.core.utils.paths.hosting_description_web_files import HostingDescriptionWebFiles
from parallels.plesk.source.custom import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.utils.paths.copy_web_content import BaseWebPathConverter
from parallels.core.utils.windows_utils import convert_path_to_cygwin
from parallels.core.windows_rsync import RsyncInstaller

logger = get_logger(__name__)


class CustomPanelCopyWindowsWebContentLocal(SubscriptionAction):
    """Put Windows web files that are already on target server to correct locations

    This action is applicable when access to the server was not provided in configuration file, and customer
    has already copied files from the source server to the target server
    """

    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_COPY_LOCAL_WEB_FILES

    def get_failure_message(self, global_context, subscription):
        """Get message for situation when action failed

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return messages.FAIL_TO_COPY_LOCAL_WEB_FILES

    def filter_subscription(self, global_context, subscription):
        """Check if we should run this action on given subscription or not

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.plesk.source.custom.migrated_subscription.CustomPanelMigratedSubscription
        """
        # skip this action for subscriptions w/o web or for unix subscriptions
        if subscription.web_target_server is None or not subscription.web_target_server.is_windows():
            return False

        return (
            global_context.conn.has_hosting_description_config(subscription.model.source)
            and
            # source web server is not specified, we don't have access to it, so
            # if customer has specified some files to copy, we consider that they all are already
            # on the target server
            subscription.web_source_server is None
            and
            subscription.web_target_server is not None
        )

    def run(self, global_context, subscription):
        """Run action on given subscription

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.plesk.source.custom.migrated_subscription.CustomPanelMigratedSubscription
        """
        files = HostingDescriptionWebFiles().list_files_to_copy(global_context, subscription)

        rsync_installer = RsyncInstaller(subscription.web_target_server)
        base_path = rsync_installer.install()
        server = subscription.web_target_server

        for item in files:
            cmd = u'{rsync_bin} --no-perms -t -r --timeout=30 {source_path} {target_path}'
            args = dict(
                rsync_bin=ntpath.join(base_path, 'panel-migrator-rsync.exe'),
                source_path=LocalSourceRsyncWebPathConverter().expand(item.source_path, server),
                target_path=WindowsTargetWebPathConverter(is_rsync=True).expand(item.target_path, server)
            )
            if item.exclude is not None and len(item.exclude) > 0:
                for i, ex in enumerate(item.exclude):
                    cmd += u" --exclude {exclude%i}" % i
                    args["exclude%i" % i] = item.exclude

            with subscription.web_target_server.runner() as runner:
                assert isinstance(runner, BaseRunner)
                runner.execute_command(cmd, args)


class LocalSourceRsyncWebPathConverter(BaseWebPathConverter):
    """Class to convert abstract path descriptor to concrete path for rsync on source server"""

    def expand(self, path, server):
        """Convert abstract path descriptor to concrete absolute path for source server

        :type path: parallels.core.utils.web_paths.WebHostingPath
        :rtype: str | unicode
        """
        if isinstance(path, web_paths.AbsolutePath):
            return convert_path_to_cygwin(path.absolute_path) + '/'
        else:
            assert False, messages.UNSUPPORTED_SOURCE_WEB_PATH_TYPE
