import logging
import ntpath

from parallels.common.actions.base.subscription_action import SubscriptionAction
from parallels.common.utils import subscription_filter
from parallels.common.utils import windows_utils
from parallels.common import MigrationError
from parallels.hosting_analyser.hosting_analyser import HostingAnalyser
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 "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 "Failed to copy web files for subscription '%s'" % 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 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((
				u"Rsync failed to copy files from the source (%s) to the target server (%s): %s\n"
				u"""This could happen because of a network connection issue.
				Retry copying the files with the help of the "copy-content" command."""
			) % (
				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)
		hosting_analyser = HostingAnalyser(global_context.migrator_server)
		if rsync_additional_args.count('-z') == 0 and hosting_analyser.is_compression_needed(subscription):
			rsync_additional_args.append('-z')

		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(
						"File or directory '%s' of subscription '%s' "
						"does not exist on source "
						"server %s, so it won't be transferred" % (
							item.source_filename, subscription.name,
							source_server.description()
						)
					)
					return False
				else:
					raise MigrationError(
						"Copy web content failed for subscription '%s': "
						"file or directory '%s' does not exist on source server %s" % (
							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()
