import ntpath
import posixpath

from parallels.core.registry import Registry
from parallels.core.utils.migrator_utils import get_version


class SessionDir(object):

	def __init__(self, runner_cm, session_dir):
		"""
		Constructor of migration session storage directory
		:param runner_cm: contextmanager that could create runner object
		:param str session_dir: path to the session directory
		"""
		self._runner_cm = runner_cm
		self._session_dir = session_dir
		self._instantiated = False

	def get_session_dir_path(self):
		"""
		Return path of a session directory
		:rtype: str
		"""
		self._lazy_instantiate()
		return self._session_dir

	def get_file_path(self, filename):
		"""
		Return full path to a file identified by filename
		:type filename: str
		:rtype: str
		"""
		self._lazy_instantiate()
		return self._get_file_path(filename)

	def _lazy_instantiate(self):
		if self._instantiated:
			return

		with self._runner_cm() as runner:
			if not runner.file_exists(self._session_dir):
				# session directory does not exist, so create it
				runner.mkdir(self._session_dir)
				self._create_version_file(get_version())
			else:
				# compare existing session directory version with Panel Migrator version
				version_file_name = self._get_file_path('version')
				is_backup = False
				if not runner.file_exists(version_file_name):
					is_backup = True
				else:
					version = runner.get_file_contents(version_file_name).strip()
					if version != get_version():
						is_backup = True
				if is_backup:
					# session directory version does not match with Panel Migrator version,
					# so backup all sensitive data: move it out from their regular location into
					# separate directory
					self._backup(get_version())
					self._create_version_file(get_version())

		# Set permissions even if directory already exists, to avoid issues when directory was created before
		# this object was initialized, or when if migrator was interrupted after directory was created, but
		# before we set permissions on it.
		self._set_permissions(runner)

		self._instantiated = True

	def _get_file_path(self, filename):
		return self._join_paths(self._session_dir, filename)

	def _backup(self, version):
		with self._runner_cm() as runner:
			files_to_move = []
			for file_name in runner.get_files_list(self._session_dir):
				if file_name == 'generated-passwords.yaml':
					# do not backup generated passwords file to prevent repeat of passwords generation
					continue
				if file_name.endswith('.yaml'):
					# backup all rest yaml files, because format of storing data may change
					# and become invalid after upgrade
					files_to_move.append(file_name)
				if file_name in {'migration-node.crt', 'migration-node.key', 'source-node.crt', 'source-node.key'}:
					# backup certificate-key pair to perform upgrade of agent on source Windows nodes
					files_to_move.append(file_name)
			if len(files_to_move) > 0:
				backup_path = self._join_paths(self._session_dir, '_backup_%s' % version)
				runner.mkdir(backup_path)
				for file_name in files_to_move:
					runner.move(
						self._join_paths(self._session_dir, file_name),
						self._join_paths(backup_path, file_name)
					)

	def _create_version_file(self, version):
		with self._runner_cm() as runner:
			version_file_name = self._get_file_path('version')
			if runner.file_exists(version_file_name):
				runner.remove_file(version_file_name)
			runner.upload_file_content(version_file_name, version)

	@staticmethod
	def _join_paths(*paths):
		raise NotImplementedError()

	def _set_permissions(self, runner):
		raise NotImplementedError()


class WindowsSessionDir(SessionDir):
	"""
	Migration session storage directory on Windows
	"""
	@staticmethod
	def _join_paths(*paths):
		return ntpath.join(*paths)

	def _set_permissions(self, runner):
		context = Registry.get_instance().get_context()
		if context is not None:
			config = context.config
			is_skip = \
				'skip-set-session-directory-permissions' in config.options('GLOBAL') and \
				config.getboolean('GLOBAL', 'skip-set-session-directory-permissions')
		else:
			is_skip = False

		if is_skip:
			return
		# check if setting inheritance is allowed by icacls built-in tool
		# by checking it's help
		if '/inheritance' in runner.sh('icacls /?'):
			icacl_commands = [
				'/inheritance:r',
				'/grant Administrators:(OI)(CI)F',
				'/grant SYSTEM:(OI)(CI)F'
			]
			for icacl_command in icacl_commands:
				runner.sh(
					"icacls {session-dir} %s" % (icacl_command,),
					{'session-dir': self._session_dir}
				)
		else:
			# If there is no 'inheritance' option for icacls, there is no
			# simple way to set permissions allowing to temporary migration
			# files only by Administrator. That is possible on Windows
			# 2003 Permissions will be inherited, and we consider that it
			# is a migration tool's user responsibility to set permissions
			# for a directory for temporary files
			pass


class UnixSessionDir(SessionDir):
	"""
	Migration session storage directory on Unix
	"""

	@staticmethod
	def _join_paths(*paths):
		return posixpath.join(*paths)

	def _set_permissions(self, runner):
		# TODO: set permissions on migration session directory not implemented on Unix yet
		pass