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 plesk_utils
from parallels.common.utils import windows_utils
from parallels.common import MigrationError

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):
		return "Failed to copy web files for subscription '%s'" % (subscription.name)

	def filter_subscription(self, global_context, subscription):
		return subscription_filter.windows_with_virtual_hosting(
			subscription
		)

	def run(self, global_context, subscription):
		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):
		with target_node.runner() as runner_target:
			target_vhosts_dir = plesk_utils.get_windows_vhosts_dir(runner_target)

		with global_context.windows_rsync(source_server, target_node, source_ip) as rsync:
			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_vhosts_dir, item.target_filename
						)
					),
					exclude=item.exclude
				)

	def _check_item_exists_on_source(
		self, global_context, item, source_server, subscription
	):
		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.
		"""
		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.
		"""
		raise NotImplementedError()
