from parallels.core import messages
from parallels.core.utils.common import find_only
from parallels.core.registry import Registry
from parallels.core.dump.dump import SubscriptionNotFoundException


class GlobalMigrationContext(object):
	"""
	:type target_panel_obj: parallels.core.target_panel_base.TargetPanelBase
	:type import_api: parallels.core.import_api.import_api.ImportAPI
	:type progress: parallels.core.utils.migration_progress.MigrationProgress
	:type pre_check_report: parallels.core.checking.Report
	:type session_files: parallels.core.session_files.CommonSessionFiles
	"""
	def __init__(self):
		self.conn = None  # connections to source and target servers
		self.session_files = None  # utility class to construct paths to session files
		self.migrator_server = None  # instance of MigratorServer
		self.import_api = None
		self.safe = None
		self.target_panel = None
		self.target_panel_obj = None
		self.load_raw_dump = None
		self.load_converted_dump = None
		self.load_shallow_dump = None
		self.source_servers = None
		self.migration_list_data = None
		self.ip_mapping = None
		self.iter_all_subscriptions = None
		self.options = None
		self.password_holder = None
		self.config = None
		self.target_existing_objects = None
		self.source_has_dns_forwarding = True
		self.hosting_analyzer_enabled = True
		self.ssh_key_pool = None
		self.rsync_pool = None
		self.get_rsync = None
		self.progress = None

		self.dns_forwarding_report = None
		self.pre_check_report = None
		self.post_migration_check_report = None
		self.test_services_report = None
		self.dump_agent = None

		# XXX Migrator object, for legacy actions - do not use for new code
		# avoid usage for old code
		self.migrator = None

	@property
	def target_model(self):
		return self.migrator._load_target_model()

	def has_subscription(self, name):
		for subscription in self.iter_all_subscriptions():
			if subscription.name == name:
				return True
		return False

	def get_subscription(self, name):
		for subscription in self.iter_all_subscriptions():
			if subscription.name == name:
				return subscription
		raise SubscriptionNotFoundException(name)

	def get_source_info(self, source_id):
		"""
		:raises: Exception
		:rtype: parallels.core.global_context.SourceInfo
		"""
		return find_only(
			self._sources_info, lambda s: s.id == source_id,
			messages.UNABLE_RETRIEVE_SOURCE_INFO_S % source_id
		)

	def get_sources_info(self, source_ids=None):
		"""Return information about sources that could have backup dumps filtered by specified list

		See _sources_info function documentation for more details.

		:rtype: list[parallels.core.global_context.SourceInfo]
		"""
		if source_ids is None:
			return self._sources_info
		else:
			return [source_info for source_info in self._sources_info if source_info.id in source_ids]

	def get_source_servers_info(self):
		"""Information about hosting servers that are considered primary source of hosting information

		In case of source Expand, it includes only Plesk servers.
		Be careful: centralized mail servers are not included in this list! They could have backup dump,
		but they are not primary sources: centralized mail server backup dumps are used only to
		restore mail settings, but not customers, resellers, web hosting settings, etc.

		In case of source legacy panels, there is usually only one source server which
		has one backup dump used as source of hosting information.

		In case of source Plesk, there could be one or multiple Plesk servers.

		In case of custom panel, backup dump file specified by customer is used as a source.

		:rtype list[parallels.core.global_context.SourceServerInfo]
		"""
		servers = self.get_sources_info(self.source_servers.keys())
		return [s for s in servers if isinstance(s, SourceServerInfo)]

	@property
	def _sources_info(self):
		"""Information about ALL sources of backup dumps.

		In case of source Expand, it includes both Plesk servers and centralized mail servers. As centralized mail
		servers are actually Plesk servers too, we dump hosting settings of centralized mail as backup dump.

		In case of source legacy panels, there is usually only one source server which has one backup dump.

		In case of source Plesk, there could be one or multiple Plesk servers.

		In case of custom panel, backup dump file specified by customer is used as a source.

		Here is a table that summarizes different sources of backup dumps:
		+--------------------------------------------+----------------------------+-------------+
		|              Source server                 | Is primary source of data? |  Is server? |
		+--------------------------------------------+----------------------------+-------------+
		| Expand Plesk server                        |          Yes               |    Yes      |
		| Expand centralized mail server             |          No                |    Yes      |
		| Legacy panel server (Confixx, cPanel, etc) |          Yes               |    Yes      |
		| Plesk server                               |          Yes               |    Yes      |
		| Custom panel                               |          Yes               |    No       |
		+--------------------------------------------+----------------------------+-------------+

		:rtype list[parallels.core.global_context.SourceInfo]
		"""
		return [
			SourceServerInfo(server_id, server_settings)
			for server_id, server_settings in self.migrator._get_source_servers().iteritems()
		]


class SourceInfo(object):
	"""Class representing all information we have about migration source."""
	def __init__(self, source_id):
		self._id = source_id

	def load_raw_dump(self):
		raise NotImplementedError()

	@property
	def id(self):
		"""Identifier of the source according to config.ini"""
		return self._id

	@property
	def ip(self):
		return None

	@property
	def is_windows(self):
		return None


class SourceServerInfo(SourceInfo):
	def __init__(self, source_id, server_settings):
		super(SourceServerInfo, self).__init__(source_id)
		self._settings = server_settings

	def load_raw_dump(self):
		return Registry.get_instance().get_context().load_raw_dump(self._settings)

	@property
	def ip(self):
		return self._settings.ip

	@property
	def is_windows(self):
		return self._settings.is_windows

	@property
	def settings(self):
		return self._settings
