from parallels.common import messages
from parallels.common import checking
from parallels.common.plesk_backup.plesk_backup_xml import SubscriptionNotFoundException
from parallels.common.utils import plesk_api_utils
from parallels.utils import find_only, if_not_none
from parallels.utils import cached


class MigratedSubscription(object):
	"""Objects that represents subscription that we are migrating"""

	def __init__(self, migrator, name):
		self._migrator = migrator
		self._name = name

	@property
	def name_idn(self):
		return self._name.encode('idna')

	@property
	def name(self):
		return self._name

	@property
	def is_windows(self):
		"""Whether the subscription is Windows or Unix"""
		# implementation is not straightforward because of H-Sphere:
		# we can have both Windows and Unix subscriptions in backup
		return self._migrator._subscription_is_windows(
			self._name, self.model.source
		)

	@property
	def is_fake(self):
		"""Check if domain is fake - created by technical reasons

		Fake domains may be not existent on source server, and so they should
		not checked.
		"""
		return self._migrator._is_fake_domain(self._name)

	# ====================== Subscription backups =============================

	@property
	def full_raw_backup(self):
		return self._migrator.get_raw_plesk_backup(self.model.source)

	@property
	def raw_backup(self):
		"""Return subscription from raw backup
		
		Raw backup contains subscriptions like they are
		presented on source panel.

		:rtype: parallels.common.plesk_backup.data_model.Subscription | parallels.common.plesk_backup.data_model.Subscription8
		"""
		return self.full_raw_backup.get_subscription(self._name)

	@property
	def raw_mail_backup(self):
		mail_server_id = self._migrator._get_mail_server_id(self.model.source)
		if mail_server_id == self.model.source:
			# if mail source and subscription source is the same, return raw subscription backup
			return self.raw_backup
		backup = self._migrator.get_raw_backup(mail_server_id)
		try:
			return backup.get_subscription(self._name)
		except SubscriptionNotFoundException:
			# subscription is not presented on centralized mail server
			# so we consider it has no mail
			return None

	@property
	def full_converted_backup(self):
		"""Return main converted backup where subscription is contained

		Backup is an object from parallels.common.plesk_backup.PleskBackupSource*
		"""
		return self._migrator.get_converted_plesk_backup(self.model.source)

	@property
	def converted_backup(self):
		"""Return subscription from converted backup

		Converted backup contains subscription how 
		we are going to restore them on the target panel.
		So IP addresses are changed, unsupported features are removed,
		etc.

		:rtype: parallels.common.plesk_backup.data_model.Subscription
		"""
		return self.full_converted_backup.get_subscription(self._name)

	@property
	def full_converted_mail_backup(self):
		"""Return mail converted backup where subscription is contained

		Backup is an object from parallels.common.plesk_backup.PleskBackupSource*
		"""
		mail_server_id = self._migrator._get_mail_server_id(self.model.source)
		return self._migrator.get_converted_backup(mail_server_id)

	@property
	def converted_mail_backup(self):
		try:
			return self.full_converted_mail_backup.get_subscription(self._name)
		except SubscriptionNotFoundException:
			# subscription is not presented on centralized mail server
			# so we consider it has no mail
			return None

	# ====================== Target data models ===============================

	@property
	def model(self):
		"""Return target data model subscription
		
		:rtype: parallels.common.target_data_model.Subscription
		"""
		ppa_model = self._migrator._load_ppa_model(False)

		return find_only(
			ppa_model.iter_all_subscriptions(),
			lambda s: s.name == self._name,
			messages.FAILED_FIND_SUBSCRIPTION_BY_NAME_2)

	@property
	def model_client(self):
		ppa_model = self._migrator._load_ppa_model(False)

		return find_only(
			ppa_model.iter_all_clients(),
			lambda c: self._name in {s.name for s in c.subscriptions},
			messages.FAILED_FIND_CLIENT_THAT_OWNS_SUBSCRIPTION % self._name
		)

	@property
	def model_reseller(self):
		ppa_model = self._migrator._load_ppa_model(False)
		for reseller in ppa_model.resellers.itervalues():
			if self.model_client in reseller.clients:
				return reseller

		return None

	# ====================== IP addresses =====================================

	@property
	def target_mail_ip(self):
		"""Return target mail server IP"""
		subscription_target_services = \
			self._migrator._get_subscription_target_services_cached()
		return if_not_none(
			if_not_none(
				subscription_target_services.get(self._name),
				lambda addresses: addresses.mail_ips
			),
			lambda mail_ips: mail_ips.v4
		)

	@property
	def target_web_ip(self):
		"""Return target web server IP"""
		subscription_target_services = \
			self._migrator._get_subscription_target_services_cached()
		return if_not_none(
			if_not_none(
				subscription_target_services.get(self._name),
				lambda addresses: addresses.web_ips
			),
			lambda web_ips: web_ips.v4
		)

	@property
	def source_web_ip(self):
		return self._migrator._get_subscription_content_ip(
			self.model
		)

	@property
	def source_mail_ip(self):
		return self._get_source_mail_ip_by_type('v4')

	@property
	def source_mail_ipv6(self):
		return self._get_source_mail_ip_by_type('v6')

	def _get_source_mail_ip_by_type(self, ip_type):
		source_servers = self._migrator._get_source_servers()
		mail_server_id = self._migrator._get_mail_server_id(self.model.source)
		if mail_server_id not in source_servers:
			return None
		mail_server_ips = self._migrator._get_mailserver_ip_by_subscription_name(
			source_servers[mail_server_id],
			self._name
		)
		if ip_type == 'v4':
			return mail_server_ips.v4
		elif ip_type == 'v6':
			return mail_server_ips.v6
		else:
			raise Exception('Unknown IP-address type "%s"' % ip_type)

	@property
	def target_public_mail_ipv4(self_):
		return self_._get_public_ip(self_.target_mail_ip)

	@property
	def target_public_mail_ipv6(self_):
		return self_._get_public_ip(self_.target_mail_ipv6)

	@property
	def target_public_web_ipv4(self_):
		return self_._get_public_ip(self_.target_web_ip)

	@property
	def target_public_web_ipv6(self_):
		return self_._get_public_ip(self_.target_web_ipv6)

	@property
	def target_mail_ipv6(self):
		subscription_target_services = \
			self._migrator._get_subscription_target_services_cached()
		return if_not_none(
			if_not_none(
				subscription_target_services.get(self._name),
				lambda addresses: addresses.mail_ips
			),
			lambda mail_ips: mail_ips.v6
		)

	@property
	def target_web_ipv6(self):
		subscription_target_services = \
			self._migrator._get_subscription_target_services_cached()
		return if_not_none(
			if_not_none(
				subscription_target_services.get(self._name),
				lambda addresses: addresses.web_ips
			),
			lambda web_ips: web_ips.v6
		)

	@property
	def target_dns_ips(self):
		"""Return IP addresses of target DNS servers"""
		return self._migrator._get_target_panel_api().get_domain_dns_server_ips(self.panel_target_server, self._name)

	@property
	def source_dns_ips(self):
		"""Return IP addresses of source DNS servers"""
		return self._migrator._get_source_dns_ips(self.model.source)

	# ====================== Server objects ===================================

	@property
	def panel_target_server(self):
		return self._migrator._get_target_panel().get_subscription_plesk_node(
			self._migrator.global_context, self._name
		)

	@property
	def web_target_server(self):
		return self._migrator._get_subscription_nodes(self._name).web

	@property
	def management_source_server(self):
		"""Return management node of source server"""
		return self.web_source_server

	@property
	def web_source_server(self):
		"""
		:rtype: parallels.common.connections.source_server.SourceServer
		"""
		return self._migrator._get_source_web_node(self._name)

	@property
	def mail_target_server(self):
		return self._migrator._get_subscription_nodes(self._name).mail

	@property
	def mail_source_server(self):
		return self._migrator._get_source_mail_node(self._name)

	@property
	def db_target_servers(self):
		return self._migrator._get_subscription_nodes(self._name).database

	@property
	def dns_target_servers(self):
		return self._migrator._get_subscription_nodes(self._name).dns

	# =============== Subscription status (suspended or active) ===============

	@property
	@cached
	def suspended_source(self):
		source_server = self.web_source_server
		return self._migrator._is_subscription_suspended_on_source(
			source_server, self._name
		)

	@property
	def suspended_target(self):
		return plesk_api_utils.is_subscription_suspended(
			self.panel_target_server.plesk_api(), self._name
		)

	# =============== Additional auxiliary functions ==========================

	@property
	@cached
	def target_sysuser_name(self):
		return plesk_api_utils.get_subscription_sysuser_name(
			self.panel_target_server.plesk_api(), self.name
		)

	@property
	def is_mail_assimilate(self):
		"""If mail should be migrated in assimilate mode for that subscription

		This is necessary for PPA SmarterMail assimilation scenario.
		"""
		return (
			(self.source_mail_ip is not None and self.source_mail_ip == self.target_mail_ip)
			or
			(self.source_mail_ipv6 is not None and self.source_mail_ipv6 == self.target_mail_ipv6)
		)

	def add_report_issue(self, report, problem, solution):
		plain_report = checking.PlainReport(report, *self._migrator._extract_source_objects_info())
		plain_report.add_subscription_issue(
			self.model.source, self._name, problem, solution
		)

	def _get_public_ip(self, ip):
		ip_to_public_ip = {
			ip.ip_address: ip.public_ip_address or ip.ip_address
			for ip in self.panel_target_server.get_all_ips(self._migrator.global_context)
		}
		return ip_to_public_ip.get(ip)
