from collections import namedtuple

from .. import core
from parallels.core.utils.common import if_not_none
from parallels.core.utils.common.xml import text_elem, elem, seq, xml_bool

PlanInfo = namedtuple('PlanInfo', ('plan_id', 'name', 'owner_id'))

PhysicalHostingDescriptor = namedtuple('PhysicalHostingDescriptor', (
	# list[PhysicalHostingProperty]
	'properties',
))

PhysicalHostingProperty = namedtuple('PhysicalHostingProperty', ('name',))

PermissionDescriptor = namedtuple('PermissionDescriptor', (
	# list[PermissionProperty]
	'properties',
))
PermissionProperty = namedtuple('PermissionProperty', ('name',))

LimitDescriptor = namedtuple('LimitDescriptor', (
	# list[LimitProperty]
	'properties',
))
LimitProperty = namedtuple('LimitProperty', ('name',))

ServicePlanLimits = namedtuple('ServicePlanLimits', (
	'overuse', 
	'limits'  # list of ServicePlanLimit
))
ServicePlanLimit = namedtuple('ServicePlanLimit', ('name', 'value',))
ServicePlanPermission = namedtuple('ServicePlanPermission', ('name', 'value',))
ServicePlanPHPSetting = namedtuple('ServicePlanPHPSetting', ('name', 'value',))
ServicePlanHostingSetting = namedtuple('ServicePlanHostingSetting', ('name', 'value',))
ServicePlanLogRotation = namedtuple('ServicePlanLogRotation', (
	'status',  # bool
	'settings',  # ServicePlanLogRotationSettings
))
ServicePlanLogRotationSettings = namedtuple('ServicePlanLogRotationSettings', (
	'log_condition',  # ServicePlanLogRotationConditionBySize | ServicePlanLogRotationConditionByTime
	'log_max_num_files', 'log_compress', 'log_email'
))
ServicePlanLogRotationConditionBySize = namedtuple('ServicePlanLogRotationConditionBySize', (
	'size',  # size in Kb
))
ServicePlanLogRotationConditionByTime = namedtuple('ServicePlanLogRotationConditionByTime', (
	'time',  # Daily | Weekly | Monthly
))
ServicePlanMail = namedtuple('ServicePlanMail', (
	# ServicePlanMailNonexistentUserBounce | ServicePlanMailNonexistentUserForward |
	# ServicePlanMailNonexistentUserReject
	'nonexistent_user',
	# bool
	'webmail',
))
ServicePlanMailNonexistentUserBounce = namedtuple('ServicePlanMailNonexistentUserBounce', (
	'message'
))
ServicePlanMailNonexistentUserForward = namedtuple('ServicePlanMailNonexistentUserForward', (
	'address'
))
ServicePlanApsPackage = namedtuple('ServicePlanApsPackage', (
	# basestring
	'name',
	# basestring
	'value',
))


class ServicePlanMailNonexistentUserReject(object):
	pass

ServicePlanPreferences = namedtuple('ServicePlanPreferences', (
	# int
	'stat',
	# boolean
	'maillists',
	# boolean
	'mailservice',
	# basestring, 'master' | 'slave'
	'dns_zone_type',
))

ServicePlanPerformance = namedtuple('ServicePlanPerformance', ('bandwidth', 'max_connections',))
ServicePlanWebServerSettings = namedtuple('ServicePlanWebServerSettings', (
	'additional', 'additional_ssl', 'additional_nginx'
))


class ServicePlanOperator(object):
	FilterAll = core.FilterAll
	FilterById = core.declare_filter('FilterById', 'id')
	FilterByName = core.declare_filter('FilterByName', 'name')
	FilterByGuid = core.declare_filter('FilterByGuid', 'guid')

	class Get(core.operation_with_filter('Get', ('owner_id', 'owner_all',))):
		operator_name = 'service-plan'
		operation_name = 'get'
		min_api_version = '1.6.3.0'
		max_api_version = None

		def data_xml(self):
			return seq(
				text_elem('owner-id', self.owner_id),
				elem('owner-all') if self.owner_all else None
			)

		@classmethod
		def parse(cls, elem):
			return [core.Result.parse(r, cls._parse_data) for r in elem.findall('result')]

		@classmethod
		def _parse_data(cls, elem):
			plan_id = int(elem.findtext('id'))
			name = elem.findtext('name')
			owner_id = if_not_none(elem.findtext('owner-id'), int)
			return PlanInfo(plan_id=plan_id, name=name, owner_id=owner_id)

	class Add(core.operation('Add', (
		'name', 'owner_login', 'limits', 'permissions', 'php_settings',
		'log_rotation', 'mail', 'preferences', 'hosting', 'performance',
		'web_server_settings',
	))):
		operator_name = 'service-plan'
		operation_name = 'add'
		min_api_version = '1.6.3.0'
		max_api_version = None

		def inner_xml(self):
			return seq(
				text_elem('name', self.name),
				text_elem('owner-login', self.owner_login),
				self._mail_xml(),
				self._limits_xml(),
				self._log_rotation_xml(),
				self._preferences_xml(),
				self._hosting_xml(),
				self._performance_xml(),
				self._permissions_xml(),
				self._php_settings_xml(),
				self._web_server_settings_xml()
			)

		def _limits_xml(self):
			return elem('limits', seq(text_elem('overuse', self.limits.overuse)) + [
				elem('limit', [text_elem('name', limit.name), text_elem('value', limit.value)])
				for limit in self.limits.limits
			])

		def _log_rotation_xml(self):
			if self.log_rotation is None:
				return None

			if self.log_rotation.status:
				items = []
				if isinstance(self.log_rotation.settings.log_condition, ServicePlanLogRotationConditionBySize):
					log_condition_node = text_elem('log-bysize', self.log_rotation.settings.log_condition.size)
				elif isinstance(self.log_rotation.settings.log_condition, ServicePlanLogRotationConditionByTime):
					log_condition_node = text_elem('log-bytime', self.log_rotation.settings.log_condition.time)
				else:
					assert False

				items.append(elem('log-condition', [log_condition_node]))
				items.append(text_elem('log-max-num-files', self.log_rotation.settings.log_max_num_files))
				if self.log_rotation.settings.log_compress is not None:
					items.append(text_elem('log-compress', xml_bool(self.log_rotation.settings.log_compress)))
				items.append(text_elem('log-email', self.log_rotation.settings.log_email))
				log_rotation_xml = elem('on', seq(*items))
			else:
				log_rotation_xml = elem('off')

			return elem('log-rotation', [log_rotation_xml])

		def _mail_xml(self):
			if self.mail is None:
				return None

			if self.mail.nonexistent_user is not None:
				if isinstance(self.mail.nonexistent_user, ServicePlanMailNonexistentUserBounce):
					nonexistent = text_elem('bounce', self.mail.nonexistent_user.message)
				elif isinstance(self.mail.nonexistent_user, ServicePlanMailNonexistentUserForward):
					nonexistent = text_elem('forward', self.mail.nonexistent_user.address)
				elif isinstance(self.mail.nonexistent_user, ServicePlanMailNonexistentUserReject):
					nonexistent = elem('reject')
				else:
					assert False
			else:
				nonexistent = None

			return elem('mail', seq(
				if_not_none(nonexistent, lambda e: elem('nonexistent-user', [e])),
				text_elem('webmail', self.mail.webmail),
			))

		def _preferences_xml(self):
			if self.preferences is None:
				return None

			return elem('preferences', seq(
				# TODO: Validate domain stat ttl: it should be greater than or equal the default server value
				# skip it before such validation will be implemented: plesk unable to create plan,
				# if specified domain stat ttl less than default server value
				# text_elem('stat', self.preferences.stat),
				text_elem('maillists', if_not_none(self.preferences.maillists, xml_bool)),
				text_elem('mailservice', if_not_none(self.preferences.mailservice, xml_bool)),
				text_elem('dns_zone_type', self.preferences.dns_zone_type)
			))

		def _hosting_xml(self):
			if self.hosting is None:
				return None

			return elem('hosting', seq(*[
				elem('property', [text_elem('name', prop.name), text_elem('value', prop.value)])
				for prop in self.hosting
			]))

		def _performance_xml(self):
			if self.performance is None:
				return None

			return elem('performance', seq(
				text_elem('bandwidth', self.performance.bandwidth),
				text_elem('max_connections', self.performance.max_connections)
			))

		def _permissions_xml(self):
			return elem('permissions', [
				elem('permission', [text_elem('name', permission.name), text_elem('value', permission.value)])
				for permission in self.permissions
			])

		def _php_settings_xml(self):
			if self.php_settings is None:
				return None

			return elem(
				'php-settings', [
					elem('setting', [text_elem('name', setting.name), text_elem('value', setting.value)])
					for setting in self.php_settings
				]
			)

		def _web_server_settings_xml(self):
			if self.web_server_settings is None:
				return None

			return elem('web-server-settings', seq(
				text_elem('additional', self.web_server_settings.additional),
				text_elem('additional-ssl', self.web_server_settings.additional_ssl),
				text_elem('additional-nginx', self.web_server_settings.additional_nginx),
			))

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'))

	class GetPhysicalHostingDescriptor(core.operation_with_filter('GetPhysicalHostingDescriptor')):
		operator_name = 'service-plan'
		operation_name = 'get-physical-hosting-descriptor'
		min_api_version = '1.6.3.0'
		max_api_version = None

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'), cls._parse_data)

		@classmethod
		def _parse_data(cls, elem):
			return PhysicalHostingDescriptor(
				properties=[
					PhysicalHostingProperty(prop_node.findtext('name'))
					for prop_node in elem.findall('descriptor/property')
				]
			)

	class GetPermissionDescriptor(core.operation_with_filter('GetPermissionDescriptor')):
		operator_name = 'service-plan'
		operation_name = 'get-permission-descriptor'
		min_api_version = '1.6.3.0'
		max_api_version = None

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'), cls._parse_data)

		@classmethod
		def _parse_data(cls, elem):
			return PermissionDescriptor(
				properties=[
					PermissionProperty(prop_node.findtext('name'))
					for prop_node in elem.findall('descriptor/property')
				]
			)

	class GetLimitDescriptor(core.operation_with_filter('GetLimitDescriptor')):
		operator_name = 'service-plan'
		operation_name = 'get-limit-descriptor'
		min_api_version = '1.6.3.0'
		max_api_version = None

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'), cls._parse_data)

		@classmethod
		def _parse_data(cls, elem):
			return LimitDescriptor(
				properties=[
					LimitProperty(prop_node.findtext('name'))
					for prop_node in elem.findall('descriptor/property')
				]
			)

	class EnableApsFilter(core.operation_with_filter('EnableApsFilter', ('owner_login',))):
		operator_name = 'service-plan'
		operation_name = 'enable-aps-filter'
		min_api_version = '1.6.3.0'
		max_api_version = None

		def data_xml(self):
			return seq(text_elem('owner-login', self.owner_login))

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'))

	class AddPackage(core.operation_with_filter('AddPackage', ('owner_login', 'packages',))):
		operator_name = 'service-plan'
		operation_name = 'add-package'
		min_api_version = '1.6.3.0'
		max_api_version = None

		def data_xml(self):
			return seq(text_elem('owner-login', self.owner_login)) + [
				elem('package', [
					text_elem('name', package.name),
					text_elem('value', package.value)
				])
				for package in self.packages
			]

		@classmethod
		def parse(cls, elem):
			return core.Result.parse(elem.find('result'))