from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.runners.base import BaseRunner
from parallels.core.utils.restore_hosting_utils import restore_hosting_settings_lock
from parallels.core.utils.common.threading_utils import synchronized_by_lock

logger = get_logger(__name__)


class TransferVirtualDirectoriesBase(SubscriptionAction):
    def get_description(self):
        return "Transfer virtual directories"

    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 filter_subscription(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        # filter only subscriptions on windows physical servers
        source_info = subscription.get_source_info()
        if not source_info.is_server or not source_info.is_windows:
            return False

        # filter only subscriptions w/ virtual hosting
        if not subscription.converted_dump.is_virtual_hosting:
            logger.debug(messages.SKIP_TRANSFER_VIRTUAL_DIRECTORIES_FOR_SUBSCRIPTION % subscription.name)
            return False

        return True

    def get_failure_message(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return messages.FAILED_TRANSFER_VIRTUAL_DIRECTORIES_FOR_SUBSCRIPTION % subscription.name

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        for site in subscription.converted_dump.iter_domains():
            if not site.is_virtual_hosting:
                logger.debug(
                    messages.SKIP_TRANSFER_VDIRS_FOR_NO_HOSTING % (
                        site.name, subscription.name
                    )
                )
                continue

            if site.hosting_type == 'sub_hst':
                logger.debug(
                    messages.SKIP_TRANSFER_VDIRS_FOR_SUBDOMAIN_ON_SUBFOLDER % (
                        site.name, subscription.name
                    )
                )
                continue

            with global_context.safe.try_subscription(
                    subscription.name,
                    messages.TRANSFER_VIRTUAL_DIRECTORIES_SITE_S % site.name,
                    is_critical=False
            ):
                self._transfer_site_vdirs(global_context, subscription, site)

    def _transfer_site_vdirs(self, global_context, subscription, site):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        # Fetch information about virtual directories
        vdir_info = self._get_site_vdirs_xml(global_context, subscription, site)

        if vdir_info is None:
            logger.debug(
                messages.NO_VIRTUAL_DIRECTORIES_INFORMATION_FOR_SITE % site.name
            )
            return

        # Restore virtual directories
        self._restore_vdir_xml(subscription, site, vdir_info)

    @staticmethod
    # we need to use this lock because calling "websrvmng" when Plesk restore is running could
    # result in "Access is denied. (Error code 5) at create mutex Global" error
    @synchronized_by_lock(lock=restore_hosting_settings_lock)
    def _restore_vdir_xml(subscription, site, vdir_info):
        """
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        server = subscription.web_target_server
        with server.runner() as runner:
            assert isinstance(runner, BaseRunner)
            vdir_info_filename = server.get_session_file_path('vdirs.xml')
            runner.upload_file_content(vdir_info_filename, vdir_info)
            runner.execute_command(
                '{websrvmng_path} --update-vdirs --vhost-name={vhost_name} --vdir-config={vdir_config_file}',
                dict(
                    websrvmng_path=server.websrvmng_bin,
                    vhost_name=site.name.encode('idna'),
                    vdir_config_file=vdir_info_filename
                )
            )

    def _get_site_vdirs_xml(self, global_context, subscription, site):
        """Get virtual directories XML for specified site

        Override in child classes.

        Returned virtual directories XML should contain all
        paths according to the target server, not source server.
        Returns XML as string.
        """
        return None
