"""Utilities for working with IP addresses"""

from parallels.core.utils.common.ip import is_ipv4, is_ipv6


class IPAddressType(object):
	"""Adapter to work with IPv4 and IPv6 addresses in a single way"""

	@property
	def title(self):
		"""Title of IP address type ("IPv4" or "IPv6")

		:rtype: str
		"""
		raise NotImplementedError()

	@staticmethod
	def is_valid_ip(ip):
		"""Check if specified string is correct address of that type

		:type ip: basestring
		:rtype: bool
		"""
		raise NotImplementedError()

	@staticmethod
	def get_for_backup_subscription(subscription):
		"""Get IP address of that type for subscription

		:type subscription: parallels.core.dump.data_model.Subscription
		:rtype: basestring
		"""
		raise NotImplementedError()

	@staticmethod
	def get_type_for_backup_subscription(subscription):
		"""Get IP address type (shared or exclusive) of that type for subscription

		:type subscription: parallels.core.dump.data_model.Subscription
		:rtype: basestring
		"""
		raise NotImplementedError()

	@staticmethod
	def set_for_target_model_subscription(subscription, new_ip):
		"""Set IP address of that type for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type new_ip: basestring
		:rtype: None
		"""
		raise NotImplementedError()


	@staticmethod
	def set_type_for_target_model_subscription(subscription, ip_type):
		"""Set IP address type for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type ip_type: basestring
		:rtype: None
		"""
		raise NotImplementedError()


	@staticmethod
	def get_for_ip_mapping(mapping):
		"""Get IP address of that type from migration list IP mapping

		:type mapping: parallels.core.migration_list.SubscriptionIPMapping
		:rtype: basestring
		"""
		raise NotImplementedError()

	def get_default_ip(self, backup):
		"""Get default IP address on the source node according to Plesk backup

		:type backup: parallels.core.dump.dump.PleskBackupSourceBase
		:rtype: basestring
		"""
		# First, try explicitly specified IP address
		if backup.get_default_ip() is not None:
			if self.is_valid_ip(backup.get_default_ip()):
				return backup.get_default_ip()

		# If default IP is not explicitly specified in backup, fallback
		# to the first shared IP of that type from backup
		system_ips = [
			ip.address for ip in backup.get_system_ips()
			if ip.type == 'shared'
		]
		system_ips_of_type = [
			ip for ip in system_ips if self.is_valid_ip(ip)
		]
		if len(system_ips_of_type) > 0:
			return system_ips_of_type[0]

		# Return None if no IP of corresponding type was found
		return None


class IPv4AddressType(IPAddressType):
	"""Adapter over IPv4 address to manage IPv4 and IPv6 addresses in a single way"""

	@property
	def title(self):
		return 'IPv4'

	@staticmethod
	def is_valid_ip(ip):
		"""Check if specified string is correct address of that type

		:type ip: basestring
		:rtype: bool
		"""
		return is_ipv4(ip)

	@staticmethod
	def get_for_backup_subscription(subscription):
		"""Get IPv4 address of subscription

		:type subscription: parallels.core.dump.data_model.Subscription
		:rtype: basestring
		"""
		return subscription.ip

	@staticmethod
	def get_type_for_backup_subscription(subscription):
		"""Get IP address type (shared or exclusive) of that type for subscription

		:type subscription: parallels.core.dump.data_model.Subscription
		:rtype: basestring
		"""
		return subscription.ip_type

	@staticmethod
	def set_for_target_model_subscription(subscription, new_ip):
		"""Set IPv4 address for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type new_ip: basestring
		:rtype: None
		"""
		subscription.web_ip = new_ip


	@staticmethod
	def set_type_for_target_model_subscription(subscription, ip_type):
		"""Set IP address type for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type ip_type: basestring
		:rtype: None
		"""
		subscription.web_ip_type = ip_type

	@staticmethod
	def get_for_ip_mapping(mapping):
		"""Get IPv4 address from migration list IP mapping

		:type mapping: parallels.core.migration_list.SubscriptionIPMapping
		:rtype: basestring
		"""
		return mapping.v4


class IPv6AddressType(IPAddressType):
	"""Adapter over IPv6 address to manage IPv4 and IPv6 addresses in a single way"""

	@property
	def title(self):
		return 'IPv6'

	@staticmethod
	def is_valid_ip(ip):
		"""Check if specified string is correct address of that type

		:type ip: basestring
		:rtype: bool
		"""
		return is_ipv6(ip)

	@staticmethod
	def get_for_backup_subscription(subscription):
		"""Get IPv6 address of subscription

		:type subscription: parallels.core.dump.model.Subscription
		:rtype: basestring
		"""
		return subscription.ipv6

	@staticmethod
	def get_type_for_backup_subscription(subscription):
		"""Get IP address type (shared or exclusive) of that type for subscription

		:type subscription: parallels.core.dump.data_model.Subscription
		:rtype: basestring
		"""
		return subscription.ipv6_type

	@staticmethod
	def set_for_target_model_subscription(subscription, new_ip):
		"""Set IPv6 address for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type new_ip: basestring
		:rtype: None
		"""
		subscription.web_ipv6 = new_ip


	@staticmethod
	def set_type_for_target_model_subscription(subscription, ip_type):
		"""Set IPv6 address for subscription

		:type subscription: parallels.core.target_data_model.Subscription
		:type ip_type: basestring
		:rtype: None
		"""
		subscription.web_ipv6_type = ip_type

	@staticmethod
	def get_for_ip_mapping(mapping):
		"""Get IPv6 address from migration list IP mapping

		:type mapping: parallels.core.migration_list.SubscriptionIPMapping
		:rtype: basestring
		"""
		return mapping.v6


def get_ip_address_types():
	"""Get IPv4 and IPv6 address types as list

	:rtype: list[parallels.core.utils.ip_utils.IPAddressType]
	"""
	return [IPv4AddressType(), IPv6AddressType()]

