import logging
import os.path

from parallels.ppa_sn import messages
from parallels.ppa.utils.poa_utils import get_host_id_by_ip
from parallels.core.hosting_security import HostingSecurityCommand
from parallels.core.powershell import Powershell
from parallels.core.utils import plesk_utils
from . import MultipleSubscriptionOperation


logger = logging.getLogger(__name__)


class CopyWindowsFilePermissionsOperation(MultipleSubscriptionOperation):
	def __init__(self, move_context):
		self.move_context = move_context

	def run(self, try_subscription, subscriptions):
		logger.info(messages.COPY_WEB_CONTENT_FILE_PERMISSIONS)
		for subscription in subscriptions:
			with try_subscription(subscription.name):
				self.run_single(subscription)

	def run_single(self, subscription):
		logger.debug(messages.APPLY_SECURITY_FILES)
		source_ppa_host_id = get_host_id_by_ip(self.move_context.conn.poa_api(), subscription.source_ip)
		target_ppa_host_id = get_host_id_by_ip(self.move_context.conn.poa_api(), subscription.target_ip)
		sysuser_name = self.move_context.ppa_data.get_subscription_sysuser_name(subscription.name)
		with self.move_context.conn.ppa_windows_node_runner(target_ppa_host_id) as runner_target:
			HostingSecurityCommand(runner_target).update_domain_security(subscription.name, sysuser_name)

		logger.debug(messages.DEBUG_COPY_FILES_OWNERSHIP)

		owners_filename = u"owners-%s.xml" % (subscription.name.encode('idna'))

		logger.debug(messages.GET_FILES_OWNERSHIP_FROM_SOURCE_NODE)
		with self.move_context.conn.ppa_windows_node_runner(source_ppa_host_id) as runner_source:
			vhosts_dir_source = plesk_utils.get_windows_vhost_dir(runner_source, subscription.name)
			powershell_script_executor = RemotePowershellScriptExecutor(
				runner_source, self.move_context.get_windows_node_session_dir(runner_source)
			)

			try:
				file_owners_xml = GetFileOwnersScript(powershell_script_executor).get_file_owners_xml(
					base_path=vhosts_dir_source
				)
			except NonZeroExitCodeException:
				logger.warning(
					messages.FAILED_TO_GET_FILE_OWNERS_NON_ZERO_EXIT_CODE,
					subscription.name
				)
				return
			except NonEmptyStderrException:
				logger.warning(
					messages.FAILED_TO_GET_FILE_OWNERS_STDERR,
					subscription.name
				)
				return

		logger.debug(messages.APPLY_FILES_OWNERSHIP_TO_TARGET_NODE)
		with self.move_context.conn.ppa_windows_node_runner(target_ppa_host_id) as runner_target:
			target_owners_filename = self.move_context.get_windows_node_session_dir(runner_target).get_file_path(
				owners_filename
			)
			vhosts_dir_target = plesk_utils.get_windows_vhost_dir(runner_target, subscription.name)
			runner_target.upload_file_content(target_owners_filename, file_owners_xml)
			powershell_script_executor = RemotePowershellScriptExecutor(
				runner_target, self.move_context.get_windows_node_session_dir(runner_target)
			)

			try:
				SetFileOwnersScript(powershell_script_executor).apply_file_owners_xml(
					base_path=vhosts_dir_target, owners_filename=target_owners_filename
				)
			except NonZeroExitCodeException:
				logger.warning(
					messages.FAILED_TO_SET_FILE_OWNERS_NON_ZERO_EXIT_CODE,
					subscription.name
				)
			except NonEmptyStderrException:
				logger.warning(
					messages.FAILED_TO_SET_FILE_OWNERS_STDERR,
					subscription.name
				)


class RemotePowershellScriptExecutor:
	def __init__(self, runner, session_dir):
		self.runner = runner
		self.session_dir = session_dir

	def execute_script_unchecked(self, script_name, args):
		"""Execute Powershell script from 'scripts' directory on remote server with Powershell
		Arguments:
		- script_name - name of a script file in 'scripts' directory
		(which is located in the same directory as this file)
		- runner - runner object to run script on
		- args - powershell script arguments, check Powershell.execute_command for more details
		"""

		powershell = Powershell(self.runner)
		script_remote_path = self.session_dir.get_file_path(script_name)
		with open(self._get_script_local_path(script_name)) as script_fp:
			self.runner.upload_file_content(script_remote_path, script_fp.read())

		return powershell.execute_script_unchecked(script_remote_path, args)

	def _get_script_local_path(self, filename):
		import parallels
		return os.path.join(
			plesk_utils.get_migrator_root_path(parallels.ppa_sn),
			'scripts', filename)


class NonZeroExitCodeException(Exception):
	pass


class NonEmptyStderrException(Exception):
	pass


class GetFileOwnersScript(object):
	"""Wrapper around get-file-owners.ps1 script"""

	def __init__(self, remote_powershell_script_executor):
		"""Arguments:
		- remote_powershell_script_executor - instance of RemotePowershellScriptExecutor
		"""
		self.remote_powershell_script_executor = remote_powershell_script_executor

	def get_file_owners_xml(self, base_path):
		"""
		Return XML with file owners for specified path.
		"""

		exit_code, stdout, stderr = self.remote_powershell_script_executor.execute_script_unchecked(
			script_name=u"get-file-owners.ps1",
			args={'basePath': base_path.rstrip('/')}
		)

		if exit_code != 0:
			raise NonZeroExitCodeException()
		if stderr.strip() != '':
			raise NonEmptyStderrException()

		return stdout


class SetFileOwnersScript(object):
	"""Wrapper around set-file-owners.ps1 script"""

	def __init__(self, remote_powershell_script_executor):
		"""Arguments:
		- remote_powershell_script_executor - instance of RemotePowershellScriptExecutor
		"""
		self.remote_powershell_script_executor = remote_powershell_script_executor

	def apply_file_owners_xml(self, base_path, owners_filename):
		"""
		Apply specified XML with file owners (that could be created by GetFileOwnersScript) for specified path.
		"""
		exit_code, _, stderr = self.remote_powershell_script_executor.execute_script_unchecked(
			script_name=u"set-file-owners.ps1",
			args={'basePath': base_path.rstrip('/'), 'ownersFile': owners_filename}
		)

		if exit_code != 0:
			raise NonZeroExitCodeException()
		if stderr.strip() != '':
			raise NonEmptyStderrException()