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

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')
    FilterByHost = core.declare_filter('FilterByHost', 'host')

    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)
            )

    class AddToACL(core.operation_with_filter('AddToACL', ())):
        operator_name = 'dns'
        operation_name = 'add_to_acl'
        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):
            pass
