import base64

from parallels.common.converter.business_objects.plans import BackupToTargetModelPlanConverter
from parallels.common.utils.plesk_limits_permissions import \
	HOSTING_PLAN_LIMIT_NAMES, HOSTING_PLAN_PERMISSION_NAMES
from parallels.target_panel_plesk.models.target_data_model import HostingPlanSettings, HostingPlanLogRotation, \
	HostingPlanLogRotationSettings, HostingPlanLogRotationConditionByTime, HostingPlanLogRotationConditionBySize, \
	HostingPlanMail, HostingPlanMailNonexistentUserBounce, HostingPlanMailNonexistentUserForward, \
	HostingPlanMailNonexistentUserReject, HostingPlanPreferences, HostingPlanHostingSetting, HostingPlanPerformance, \
	HostingPlanWebServerSettings, HostingPlanApsBundleFilter, HostingPlanApsBundleFilterItem, HostingPlanCustomItem, \
	HostingPlanDefaultDatabaseServer
from parallels.utils import if_not_none


class BackupToTargetPleskModelPlanConverter(BackupToTargetModelPlanConverter):
	def __init__(self, allowed_limits, allowed_permissions, allowed_hosting_settings, is_windows=False):
		"""
		:type allowed_limits: set[basestring]
		:type allowed_permissions: set[basestring]
		:type allowed_hosting_settings: set[basestring]
		:type is_windows: bool
		"""
		self._allowed_limits = allowed_limits
		self._allowed_permissions = allowed_permissions
		self._allowed_hosting_settings = allowed_hosting_settings
		self._is_windows = is_windows

	def convert(self, backup_plan):
		"""Convert plan settings from Plesk backup to format ready to import with Plesk API

		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanSettings
		"""
		return HostingPlanSettings(
			overuse=self._convert_overuse(backup_plan),
			limits=self._convert_limits(backup_plan, self._allowed_limits),
			permissions=self._convert_permissions(backup_plan, self._allowed_permissions),
			php_settings_node=backup_plan.php_settings_node,
			log_rotation=self._convert_log_rotation(backup_plan),
			mail=self._convert_mail(backup_plan),
			preferences=self._convert_preferences(backup_plan),
			hosting=self._convert_hosting(backup_plan, self._allowed_hosting_settings),
			virtual_hosting_enabled=backup_plan.properties.get('vh_type', 'physical') != 'none',
			performance=self._convert_performance(backup_plan),
			web_server_settings=self._convert_web_server_settings(backup_plan),
			aps_bundle_filter=self._convert_aps_bundle_filter(backup_plan),
			custom_plan_items=self._convert_custom_plan_items(backup_plan),
			default_db_servers=self._convert_default_database_servers(backup_plan)
		)

	@staticmethod
	def _convert_overuse(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: basestring
		"""
		return backup_plan.properties.get('overuse')

	@staticmethod
	def _convert_limits(backup_plan, allowed_limits):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:type allowed_limits: set[basestring]
		:rtype: dict[string, string]
		"""
		return {
			limit_name: backup_plan.properties[limit_name]
			for limit_name in allowed_limits & set(HOSTING_PLAN_LIMIT_NAMES)
			if limit_name in backup_plan.properties
		}

	@staticmethod
	def _convert_permissions(backup_plan, allowed_permissions):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:type allowed_permissions: set[basestring]
		:rtype: dict[string, string]
		"""
		return {
			permission_name: backup_plan.properties[permission_name]
			for permission_name in allowed_permissions & set(HOSTING_PLAN_PERMISSION_NAMES)
			if permission_name in backup_plan.properties
		}

	@staticmethod
	def _convert_log_rotation(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanLogRotationSettings
		"""
		if backup_plan.log_rotation is not None:
			if backup_plan.log_rotation.period is not None:
				log_condition = HostingPlanLogRotationConditionByTime(backup_plan.log_rotation.period.capitalize())
			else:
				log_condition = HostingPlanLogRotationConditionBySize(backup_plan.log_rotation.max_size)

			log_rotation = HostingPlanLogRotation(
				status=backup_plan.log_rotation.enabled,
				settings=HostingPlanLogRotationSettings(
					log_condition=log_condition,
					log_max_num_files=backup_plan.log_rotation.max_logfiles_number,
					log_compress=backup_plan.log_rotation.compress,
					log_email=backup_plan.log_rotation.email,
				)
			)
		else:
			log_rotation = None
		return log_rotation

	def _convert_mail(self, backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanMail
		"""
		return HostingPlanMail(
			nonexistent_user=self._convert_mail_nonexistent_user(backup_plan),
			webmail=backup_plan.properties.get('webmail'),
		)

	@staticmethod
	def _convert_mail_nonexistent_user(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: None |
			parallels.target_panel_plesk.models.target_data_model.HostingPlanMailNonexistentUserBounce |
			parallels.target_panel_plesk.models.target_data_model.HostingPlanMailNonexistentUserForward |
			parallels.target_panel_plesk.models.target_data_model.HostingPlanMailNonexistentUserReject
		"""
		nonexistent_prop = backup_plan.properties.get('nonexist_mail')
		if nonexistent_prop == 'bounce':
			nonexistent_user = HostingPlanMailNonexistentUserBounce(message=backup_plan.properties.get('bounce_mess'))
		elif nonexistent_prop == 'catch':
			nonexistent_user = HostingPlanMailNonexistentUserForward(address=backup_plan.properties.get('catch_addr'))
		elif nonexistent_prop == 'reject':
			nonexistent_user = HostingPlanMailNonexistentUserReject()
		else:
			nonexistent_user = None
		return nonexistent_user

	@staticmethod
	def _convert_preferences(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanPreferences
		"""
		return HostingPlanPreferences(
			stat=backup_plan.properties.get('stat_ttl', '3'),
			maillists=backup_plan.properties.get('maillists', 'false') == 'true',
			mailservice=backup_plan.properties.get('mailservice', 'true') == 'true',
			dns_zone_type=backup_plan.properties.get('dns_type', 'master')
		)

	@staticmethod
	def _convert_hosting(backup_plan, allowed_hosting_settings):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:type allowed_hosting_settings: set[basestring]
		:rtype: list[parallels.target_panel_plesk.models.target_data_model.HostingPlanHostingSetting]
		"""
		available_hostings_list = set(allowed_hosting_settings)
		if backup_plan.properties.get('php') != 'true':
			available_hostings_list -= {'php_handler_type', 'php_handler_id'}
		hostings = []

		for name, value in backup_plan.properties.iteritems():
			if name == 'quota':
				name = 'ftp_quota'

			if name == 'managed_runtime_version' and backup_plan.properties.get('asp_dot_net') == 'false':
				# we should not set ASP.NET version if ASP.NET is disabled, otherwise Plesk fails to create plan
				continue

			if name in available_hostings_list:
				hostings.append(HostingPlanHostingSetting(
					name=name, value=value
				))

		return hostings

	def _convert_performance(self, backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanPerformance
		"""
		bandwidth = int(backup_plan.properties.get('bandwidth', '-1'))
		if bandwidth > 0 and not self._is_windows:
			bandwidth /= 1024

		return HostingPlanPerformance(
			bandwidth=str(bandwidth),
			max_connections=backup_plan.properties.get('max_connections', '-1')
		)

	@staticmethod
	def _convert_web_server_settings(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanWebServerSettings
		"""
		return HostingPlanWebServerSettings(
			additional=if_not_none(backup_plan.properties.get('additionalSettings'), base64.b64decode),
			additional_ssl=if_not_none(backup_plan.properties.get('additionalSslSettings'), base64.b64decode),
			additional_nginx=if_not_none(backup_plan.properties.get('additionalNginxSettings'), base64.b64decode),
		)

	@staticmethod
	def _convert_aps_bundle_filter(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: parallels.target_panel_plesk.models.target_data_model.HostingPlanApsBundleFilter | None
		"""
		if backup_plan.aps_filter is None:
			return None

		return HostingPlanApsBundleFilter(
			type=backup_plan.aps_filter.type,
			items=[
				HostingPlanApsBundleFilterItem(name=item.name, value=item.value)
				for item in backup_plan.aps_filter.items
			]
		)

	@staticmethod
	def _convert_custom_plan_items(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: list[parallels.target_panel_plesk.models.target_data_model.HostingPlanCustomItem]
		"""
		return [
			HostingPlanCustomItem(
				name=item.name, guid=item.guid, visible=item.visible,
				hint=item.hint, description=item.description,
				url=item.url, options=item.options
			)
			for item in backup_plan.custom_plan_items
		]

	@staticmethod
	def _convert_default_database_servers(backup_plan):
		"""
		:type backup_plan: parallels.common.plesk_backup.data_model.HostingPlan
		:rtype: list[parallels.target_panel_plesk.models.target_data_model.HostingPlanDefaultDatabaseServer]
		"""
		return [
			HostingPlanDefaultDatabaseServer(
				type=server.type, host=server.host, port=server.port
			)
			for server in backup_plan.default_db_servers
		]