from parallels.common import messages
import logging
import ntpath

from parallels.common.actions.base.subscription_action import SubscriptionAction
from parallels.common.actions.utils.multithreading_properties import MultithreadingProperties
from parallels.common.utils import subscription_filter
from parallels.common.utils import windows_utils
from parallels.common.utils.hosting_analyser_utils import apply_hosting_analyser_strategy
from parallels.common import MigrationError
from parallels.common import migrator_config

logger = logging.getLogger(__name__)


class CopyWindowsWebContentBase(SubscriptionAction):
	"""Base class to copy web content for Windows servers"""
	def __init__(self):
		# Whether to check if copied file exists on source server before
		# running rsync command for better error reporting and ability to skip
		# files/directories if they don't exist on source server
		self._check_source_file_exists = False

	def get_description(self):
		return messages.COPY_WEB_FILES_FROM_WINDOWS_SERVERS

	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_COPY_WEB_FILES_FOR_SUBSCRIPTION % 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):
		"""
		:rtype: parallels.common.actions.utils.multithreading_properties.MultithreadingProperties
		"""
		return MultithreadingProperties(can_use_threads=True)

	def filter_subscription(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		return subscription_filter.windows_with_virtual_hosting(
			subscription
		)

	def run(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		files = self._list_files_to_copy(global_context, subscription)
		try:
			self._copy_files_windows(
				global_context,
				subscription,
				files, 
				subscription.source_web_ip, 
				subscription.web_target_server, 
				subscription.web_source_server
			)
		except Exception as e:
			logger.debug(u"Exception: ", exc_info=e)
			raise MigrationError((
				messages.RSYNC_FAILED_COPY_FILES_FROM_SOURCE) % (
				subscription.web_source_server.description(), 
				subscription.web_target_server.description(), 
				str(e)
			))

	def _copy_files_windows(self, global_context, subscription, files, source_ip, target_node, source_server):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""

		rsync_additional_args = migrator_config.read_rsync_additional_args(global_context.config)
		apply_hosting_analyser_strategy(global_context, subscription, rsync_additional_args)

		rsync = global_context.get_rsync(source_server, target_node, source_ip)
		for item in files:
			if self._check_source_file_exists:
				if not self._check_item_exists_on_source(
					global_context, item, source_server, subscription
				):
					continue
			rsync.sync(
				source_path='vhosts/%s' % item.source_filename,
				target_path=windows_utils.convert_path_to_cygwin(
					windows_utils.path_join(
						target_node.vhosts_dir, item.target_filename
					)
				),
				exclude=item.exclude,
				rsync_additional_args=rsync_additional_args
			)

	def _check_item_exists_on_source(
		self, global_context, item, source_server, subscription
	):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		with source_server.runner() as source_runner:
			full_filename = ntpath.join(
				self._get_source_vhosts_dir(global_context, source_server), 
				item.source_filename
			)
			if not windows_utils.file_exists(source_runner, full_filename):
				if item.skip_if_source_not_exists:
					logger.debug(
						messages.FILE_DIRECTORY_S_SUBSCRIPTION_S_DOES % (
							item.source_filename, subscription.name,
							source_server.description()
						)
					)
					return False
				else:
					raise MigrationError(
						messages.COPY_WEB_CONTENT_FAILED_FOR_SUBSCRIPTION % (
							subscription.name, item.source_filename,
							source_server.description()
						)
					)
			else:
				return True

	def _get_source_vhosts_dir(self, global_context, source_server):
		"""Get directory where virtual hosts data is located
		
		Override in child classes.

		:type global_context: parallels.common.global_context.GlobalMigrationContext
		"""
		raise NotImplementedError()

	def _list_files_to_copy(self, global_context, subscription):
		"""Make a list of source server directories to be transferred.

		Return a list of (source directory -> destination directory) mappings.
		Override in child classes.

		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		raise NotImplementedError()
