from parallels.common import messages
import logging

from parallels.common.actions.base.subscription_action import SubscriptionAction
from parallels.common.utils.restore_hosting_utils import restore_hosting_settings_lock
from parallels.utils.threading_utils import synchronized_by_lock

logger = logging.getLogger(__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.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		if not subscription.is_windows:
			return False
		if not subscription.converted_backup.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.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		return messages.FAILED_TRANSFER_VIRTUAL_DIRECTORIES_FOR_SUBSCRIPTION % subscription.name

	def run(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		for site in subscription.converted_backup.iter_domains():
			if not site.is_virtual_hosting:
				logger.debug(
					messages.SKIP_TRANSFER_VIRTUAL_DIRECTORIES_FOR_SITE % (
						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.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.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.common.migrated_subscription.MigratedSubscription
		"""
		server = subscription.web_target_server
		with server.runner() as runner:
			vdir_info_filename = server.get_session_file_path('vdirs.xml')
			runner.upload_file_content(vdir_info_filename, vdir_info)
			runner.sh(
				'{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
