from collections import namedtuple

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

OBJECT_TYPE_SITE = 'site'
OBJECT_TYPE_ALIAS = 'alias'

DnsRecord = namedtuple('DnsRecord', ('id', 'src', 'dst', 'opt', 'rec_type'))
DnsZone = namedtuple('DnsZone', ('object_id', 'object_type', 'soa'))
DelRecResult = namedtuple('DelRecResult', ('id'))
EnableResult = namedtuple('EnableResult', ('site_id', 'site_alias_id'))
DisableResult = namedtuple('DisableResult', ('site_id', 'site_alias_id'))
Soa = namedtuple('Soa', ('ttl', 'refresh', 'retry', 'expire', 'minimum'))
MasterServerRecord = namedtuple('MasterServerRecord', ('id', 'ip_address', 'site_id', 'site_alias_id'))


class DnsOperator(object):
	class Dataset(object):
		SOA = 'soa'
		values = [SOA]

	FilterAll = core.FilterAll
	FilterById = core.declare_filter('FilterById', 'id')
	FilterBySiteId = core.declare_filter('FilterBySiteId', 'site-id')
	FilterBySiteAliasId = core.declare_filter('FilterBySiteAliasId', 'site-alias-id')

	class Set(core.operation_with_filter('Set', ('soa',))):
		operator_name = 'dns'
		operation_name = 'set'
		# The doc says "API RPC version: all versions". Assume 8.0.1 and later
		min_api_version = '1.4.1.2'
		max_api_version = None

		def data_xml(self):
			return [
				elem('soa', [
					text_elem(k, getattr(self.soa, k))
					for k in ('ttl', 'refresh', 'retry', 'expire', 'minimum')
					if getattr(self.soa, k) is not 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):
			return None

	class Get(core.operation_with_filter('Get', ('soa',))):
		operator_name = 'dns'
		operation_name = 'get'
		min_api_version = '1.4.1.2'
		max_api_version = None

		def data_xml(self):
			return seq(
				elem('soa') if self.soa 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):
			site_id = elem.findtext('site-id')
			if site_id is not None:
				object_id = int(site_id)
				object_type = OBJECT_TYPE_SITE
			else:
				object_type = OBJECT_TYPE_ALIAS
				site_alias_id = elem.findtext('site-alias-id')
				if site_alias_id is not None:
					object_id = int(site_alias_id)
				else:
					# Plesk 10.0 and 10.1.0 had bug #61827 about invalid XML response of dns.get operation
					# site_alias_id was returned instead of correct site-alias-id 
					object_id = int(elem.findtext('site_alias_id'))
			if elem.findtext('soa') is not None:
				soa = Soa(
					ttl=int(elem.findtext('soa/ttl')),
					refresh=int(elem.findtext('soa/refresh')),
					retry=int(elem.findtext('soa/retry')),
					expire=int(elem.findtext('soa/expire')),
					minimum=int(elem.findtext('soa/minimum'))
				)
			else:
				soa = None

			return DnsZone(
				object_id=object_id,
				object_type=object_type,
				soa=soa
			)

	class GetRec(core.operation_with_filter('GetRec', ('template',))):
		operator_name = 'dns'
		operation_name = 'get_rec'
		min_api_version = '1.4.1.2'
		max_api_version = None

		def data_xml(self):
			return seq(
				elem('template') if self.template 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):
			return DnsRecord(
				id=elem.findtext('id'),
				src=elem.findtext('data/host'),
				dst=elem.findtext('data/value'),
				opt=elem.findtext('data/opt'),
				rec_type=elem.findtext('data/type')
			)

	class AddRec(core.operation('AddRec', ('target', 'src', 'dst', 'opt', 'rec_type',))):
		operator_name = 'dns'
		operation_name = 'add_rec'
		min_api_version = '1.4.1.2'
		max_api_version = None

		class TargetSiteAliasId(namedtuple('TargetSiteAliasId', ('id',))):
			def inner_xml(self): return [text_elem('site-alias-id', self.id)]

		def inner_xml(self):
			return self.target.inner_xml() + seq(
				text_elem('type', self.rec_type),
				text_elem('host', self.src),
				text_elem('value', self.dst),
				text_elem('opt', self.opt) if self.opt is not None else None,
			)

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

	class DelRec(core.operation_with_filter('DelRec', ('template',))):
		operator_name = 'dns'
		operation_name = 'del_rec'
		min_api_version = '1.4.1.2'
		max_api_version = None

		FilterById = core.declare_filter('FilterById', 'id')

		def data_xml(self):
			return seq(
				elem('template') if self.template else None
			)

		@classmethod
		def parse(cls, elem):
			return core.parse_result_set(elem, cls._parse_data, 'id')

		@classmethod
		def _parse_data(cls, elem):
			return DelRecResult(
				id=elem.findtext('id'),
			)

	class Switch(core.operation_with_filter('Switch', ('zone_type',))):
		operator_name = 'dns'
		operation_name = 'switch'
		min_api_version = '1.6.3.0'	 # for Plesk 9.x and earlier use DnsOperator9
		max_api_version = None

		def data_xml(self):
			return [
				text_elem('zone_type', self.zone_type)
			]

		@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):
			return None

	class GetMasterServer(core.operation_with_filter('GetMasterServer', ())):
		operator_name = 'dns'
		operation_name = 'get_master_server'
		min_api_version = '1.6.3.0'	# for Plesk 9.x and earlier use DnsOperator9
		max_api_version = None

		@classmethod
		def parse(cls, elem):
			return core.parse_result_set(elem, cls._parse_data, 'id')

		@classmethod
		def _parse_data(cls, elem):
			return MasterServerRecord(
				id=int(elem.findtext('id')),
				ip_address=elem.findtext('ip_address'),
				site_id=if_not_none(elem.findtext('site-id'), int),
				site_alias_id=if_not_none(elem.findtext('site-alias-id'), int)
			)

	class AddMasterServer(core.operation('AddMasterServer', ('target', 'ip_address',))):
		operator_name = 'dns'
		operation_name = 'add_master_server'
		min_api_version = '1.6.3.0'	# for Plesk 9.x and earlier use DnsOperator9
		max_api_version = None

		class TargetSiteId(namedtuple('TargetSiteId', ('id',))):
			def inner_xml(self): return [text_elem('site-id', self.id)]

		class TargetSiteAliasId(namedtuple('TargetSiteAliasId', ('id',))):
			def inner_xml(self): return [text_elem('site-alias-id', self.id)]

		def inner_xml(self):
			return self.target.inner_xml() + seq(
				text_elem('ip_address', self.ip_address),
			)

		@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):
			return int(elem.findtext('id'))

	class DelMasterServer(core.operation_with_filter('DelMasterServer', ())):
		operator_name = 'dns'
		operation_name = 'del_master_server'
		min_api_version = '1.6.3.0'	# for Plesk 9.x and earlier use DnsOperator9
		max_api_version = None

		@classmethod
		def parse(cls, elem):
			return core.parse_result_set(elem, cls._parse_data, 'id')

		@classmethod
		def _parse_data(cls, elem):
			return int(elem.findtext('id'))

	class Enable(core.operation_with_filter('Enable', ())):
		operator_name = 'dns'
		operation_name = 'enable'
		min_api_version = '1.6.3.0'
		max_api_version = 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):
			return EnableResult(
				site_id=if_not_none(elem.findtext('site-id'), int),
				site_alias_id=if_not_none(elem.findtext('site-alias-id'), int)
			)

	class Disable(core.operation_with_filter('Disable', ())):
		operator_name = 'dns'
		operation_name = 'disable'
		min_api_version = '1.6.3.0'
		max_api_version = 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):
			return DisableResult(
				site_id=if_not_none(elem.findtext('site-id'), int),
				site_alias_id=if_not_none(elem.findtext('site-alias-id'), int)
			)