from collections import namedtuple
from datetime import datetime

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

from .. import core

ResellerInfo = namedtuple('ResellerInfo', (
    'gen_info', 'statistics', 'permissions', 'limits', 'ip_pool', 'subscriptions'
))

ResellerGenInfo = namedtuple('ResellerGenInfo', (
    'creation_date', 'cname', 'pname', 'login', 'status', 'phone', 'fax', 'email', 'address',
    'city', 'state', 'pcode', 'country', 'locale', 'guid', 'external_id',
))

ResellerAddGenInfo = namedtuple('ResellerAddGenInfo', (
    'cname', 'pname', 'login', 'passwd', 'status', 'phone', 'fax', 'email', 'address',
    'city', 'state', 'pcode', 'country', 'locale', 'external_id',
))
ResellerAddLimits = namedtuple('ResellerAddLimits', (
    'limits',  # list of ResellerAddLimit
    'resource_policy',  # instance of ResellerAddResourcePolicy
))
ResellerAddResourcePolicy = namedtuple('ResellerAddResourcePolicy', (
    'overuse', 'oversell',
))
ResellerAddLimit = namedtuple('ResellerAddLimit', ('name', 'value',))
ResellerAddPermission = namedtuple('ResellerAddPermission', ('name', 'value',))
ResellerAddResult = namedtuple('ResellerAddResult', (
    'reseller_id', 'guid'
))

ResellerStat = namedtuple('ResellerStat', (
    'active_clients', 'active_domains', 'subdomains', 'domain_aliases', 'disk_space', 'web_users',
    'data_bases', 'postboxes', 'mbox_quota', 'redirects', 'mail_groups', 'mail_resps', 'mail_lists',
    'webapps', 'traffic', 'traffic_prevday',
))
ResellerLimits = namedtuple('ResellerLimits', ('oversell', 'overuse', 'limits'))
SubscriptionInfo = namedtuple('SubscriptionInfo', ('locked', 'synchronized', 'plans'))
PlanReference = namedtuple('PlanReference', ('guid', 'external_id'))


class ResellerOperator(object):
    class Dataset(object):
        GEN_INFO = 'gen-info'
        STAT = 'stat'
        PERMISSIONS = 'permissions'
        LIMITS = 'limits'
        IP_POOL = 'ippool'
        SUBSCRIPTIONS = 'subscriptions'

        values = [GEN_INFO, STAT, PERMISSIONS, LIMITS, IP_POOL, SUBSCRIPTIONS]

    FilterEmpty = core.FilterAll
    FilterAll = core.FilterAllWithNode
    FilterById = core.declare_filter('FilterById', 'id')
    FilterByLogin = core.declare_filter('FilterByLogin', 'login')
    FilterByGuid = core.declare_filter('FilterByGuid', 'guid')
    FilterByExternalId = core.declare_filter('FilterByExternalId', 'external-id')

    class Get(core.operation_with_filter('Get', ('dataset',))):
        operator_name = 'reseller'
        operation_name = 'get'
        min_api_version = '1.6.4.0'
        max_api_version = None

        def data_xml(self):
            return [
                elem('dataset', [elem(value) for value in ResellerOperator.Dataset.values if value in self.dataset])
            ]
            
        @classmethod
        def parse(cls, elem):
            results = [core.Result.parse(r, cls._parse_data) for r in elem.findall('result')]
            if len(results) == 1 and results[0].ok and results[0].data is None:
                results = []
            return results
        
        @classmethod
        def _parse_data(cls, elem):
            reseller_id = if_not_none(elem.findtext('id'), int)
            if reseller_id is None:
                return None
            
            data = elem.find('data')
            gen_info = if_not_none(data.find('gen-info'), cls._parse_gen_info)
            statistics = if_not_none(data.find('stat'), cls._parse_statistics)
            permissions = if_not_none(data.find('permissions'), cls._parse_permissions)
            limits = if_not_none(data.find('limits'), cls._parse_limits)
            ip_pool = if_not_none(data.find('ippool'), cls._parse_ip_pool)
            subscriptions = if_not_none(data.find('subscriptions'), cls._parse_subscription)
            
            return (
                reseller_id,
                ResellerInfo(gen_info, statistics, permissions, limits, ip_pool, subscriptions)
            )
            
        @classmethod
        def _parse_gen_info(cls, elem):
            return ResellerGenInfo(
                datetime.strptime(elem.findtext('cr-date'), '%Y-%m-%d'),
                elem.findtext('cname'), elem.findtext('pname'), elem.findtext('login'),
                elem.findtext('status'),
                elem.findtext('phone'), elem.findtext('fax'), elem.findtext('email'),
                elem.findtext('address'), elem.findtext('city'), elem.findtext('state'),
                elem.findtext('pcode'), elem.findtext('country'), elem.findtext('locale'),
                elem.findtext('guid'), elem.findtext('external-id'),
            )
        
        @classmethod
        def _parse_statistics(cls, elem):
            return ResellerStat._make(int(elem.findtext(name.replace('_', '-'))) for name in ResellerStat._fields)
        
        @classmethod
        def _parse_permissions(cls, elem):
            return dict(
                (n.findtext('name'), n.findtext('value'))
                for n in elem.findall('permission')
            )
        
        @classmethod
        def _parse_limits(cls, elem):
            return ResellerLimits(
                if_not_none(elem.findtext('resource-policy/oversell'), lambda x: x == 'true'),
                elem.findtext('resource-policy/overuse'),
                dict((e.findtext('name'), e.findtext('value')) for e in elem.findall('limit'))
            )
        
        @classmethod
        def _parse_ip_pool(cls, elem):
            return [
                (e.findtext('ip-address'), e.findtext('ip-type'))
                for e in elem.findall('ip')
            ]
        
        @classmethod
        def _parse_subscription(cls, elem):
            return if_not_none(elem.find('subscription'), lambda e: SubscriptionInfo(
                e.findtext('locked'),
                e.findtext('synchronized'),
                [cls._parse_plan_ref(p) for p in e.findall('plan')]
            ))
            
        @classmethod
        def _parse_plan_ref(cls, elem):
            return PlanReference(
                elem.findtext('plan-guild'),
                elem.findtext('plan-external-id'),
            )

    class IppoolAddIp(core.operation('IppoolAddIp', ('reseller_id', 'ip_address', 'ip_type'))):
        operator_name = 'reseller'
        operation_name = 'ippool-add-ip'
        min_api_version = '1.6.4.0'
        max_api_version = None

        def inner_xml(self):
            return [
                text_elem('reseller-id', self.reseller_id),
                elem('ip', [
                    text_elem('ip-address', self.ip_address),
                    text_elem('ip-type', self.ip_type)
                ])
            ]

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

    class Add(core.operation('Add', (
        'gen_info',  # instance of ResellerAddGenInfo
        'limits',  # instance of ResellerAddLimits
        'permissions',  # list of ResellerAddPermission
        'plan_name'
    ))):
        operator_name = 'reseller'
        operation_name = 'add'
        min_api_version = '1.6.0.2'
        max_api_version = None

        def inner_xml(self):
            return seq( 
                elem('gen-info', seq(
                    text_elem('cname', self.gen_info.cname),
                    text_elem('pname', self.gen_info.pname),
                    text_elem('login', self.gen_info.login),
                    text_elem('passwd', self.gen_info.passwd),
                    text_elem('status', self.gen_info.status),
                    text_elem('phone', self.gen_info.phone),
                    text_elem('fax', self.gen_info.fax),
                    text_elem('email', self.gen_info.email),
                    text_elem('address', self.gen_info.address),
                    text_elem('city', self.gen_info.city),
                    text_elem('state', self.gen_info.state),
                    text_elem('pcode', self.gen_info.pcode),
                    text_elem('country', self.gen_info.country),
                    text_elem('locale', self.gen_info.locale),
                    text_elem('external-id', self.gen_info.external_id),
                )),
                elem(
                    'limits',
                    seq(
                        elem('resource-policy', seq(
                            text_elem('oversell', self.limits.resource_policy.oversell),
                            text_elem('overuse', self.limits.resource_policy.overuse),
                        )) if self.limits.resource_policy is not None else None,
                    ) +
                    [
                        elem('limit', [
                            text_elem('name', limit.name),
                            text_elem('value', limit.value)
                        ])
                        for limit in self.limits.limits
                    ]
                ) if self.limits is not None else None,
                elem('permissions', [
                    elem('permission', [
                        text_elem('name', permission.name),
                        text_elem('value', permission.value)
                    ])
                    for permission in self.permissions
                ]) if self.limits is not None else None,
                text_elem('plan-name', self.plan_name),
            )

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

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

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

        @classmethod
        def parse(cls, command_node):
            """
            :type command_node: xml.etree.ElementTree.Element
            :rtype: parallels.plesk.utils.xml_rpc.plesk.core.Success|parallels.plesk.utils.xml_rpc.plesk.core.Failure
            """
            return core.Result.parse(command_node.find('result'), cls._parse_data)

        @classmethod
        def _parse_data(cls, result_node):
            """
            :type result_node: xml.etree.ElementTree.Element
            :rtype: parallels.plesk.utils.xml_rpc.plesk.core.Descriptor
            """
            return core.Descriptor.parse(result_node.find('descriptor'))

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

        @classmethod
        def parse(cls, command_node):
            """
            :type command_node: xml.etree.ElementTree.Element
            :rtype: parallels.plesk.utils.xml_rpc.plesk.core.Success|parallels.plesk.utils.xml_rpc.plesk.core.Failure
            """
            return core.Result.parse(command_node.find('result'), cls._parse_data)

        @classmethod
        def _parse_data(cls, result_node):
            """
            :type result_node: xml.etree.ElementTree.Element
            :rtype: parallels.plesk.utils.xml_rpc.plesk.core.Descriptor
            """
            return core.Descriptor.parse(result_node.find('descriptor'))
