from collections import namedtuple

from parallels.core.utils.common.xml import seq, elem, text_elem
from parallels.core.utils.common import if_not_none
from .. import core
from .dns import DnsZone, Soa

OBJECT_TYPE_DOMAIN = 'domain'
OBJECT_TYPE_ALIAS = 'alias'

MasterServerRecord = namedtuple('MasterServerRecord', ('id', 'ip_address', 'domain_id', 'domain_alias_id'))
EnableResult = namedtuple('EnableResult', ('domain_id', 'domain_alias_id'))
DisableResult = namedtuple('DisableResult', ('domain_id', 'domain_alias_id'))


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

    FilterAll = core.FilterAll
    FilterById =  core.declare_filter('FilterById', 'id')
    FilterByDomainId = core.declare_filter('FilterByDomainId', 'domain_id')
    FilterByDomainAliasId = core.declare_filter('FilterByDomainId', 'domain_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 = '1.6.5.0'

        def data_xml(self):
            return seq(
                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) for r in elem.findall('result')]

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

        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):
            domain_id = elem.findtext('domain_id')
            if domain_id is not None:
                object_id = int(domain_id)
                object_type = OBJECT_TYPE_DOMAIN
            else:
                object_type = OBJECT_TYPE_ALIAS
                domain_alias_id = elem.findtext('domain_alias_id')
                object_id = int(domain_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 Switch(core.operation_with_filter('Switch', ('zone_type',))):
        operator_name = 'dns'
        operation_name = 'switch'
        min_api_version = '1.4.0.0'
        max_api_version = '1.6.5.0'

        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.4.0.0'
        max_api_version = '1.6.2.9'     # less than 1.6.3.0

        @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'),
                domain_id=if_not_none(elem.findtext('domain_id'), int),
                domain_alias_id=if_not_none(elem.findtext('domain_alias_id'), int)
            )

    class AddMasterServer(core.operation('AddMasterServer', ('target', 'ip_address',))):
        operator_name = 'dns'
        operation_name = 'add_master_server'
        min_api_version = '1.4.0.0'
        max_api_version = '1.6.2.9'     # less than 1.6.3.0

        class TargetDomainId(namedtuple('TargetDomainId', ('id',))):
            def inner_xml(self):
                return [text_elem('domain_id', self.id)]

        class TargetDomainAliasId(namedtuple('TargetDomainAliasId', ('id',))):
            def inner_xml(self):
                return [text_elem('domain_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.4.0.0'
        max_api_version = '1.6.2.9'  # less than 1.6.3.0

        @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.4.0.0'
        max_api_version = '1.6.2.9'  # less than 1.6.3.0

        @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(
                domain_id=if_not_none(elem.findtext('domain_id'), int),
                domain_alias_id=if_not_none(elem.findtext('domain_alias_id'), int)
            )

    class Disable(core.operation_with_filter('Disable', ())):
        operator_name = 'dns'
        operation_name = 'disable'
        min_api_version = '1.4.0.0'
        max_api_version = '1.6.2.9'  # less than 1.6.3.0

        @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(
                domain_id=if_not_none(elem.findtext('domain_id'), int),
                domain_alias_id=if_not_none(elem.findtext('domain_alias_id'), int)
            )