# -*- coding: utf-8 -*-
import logging
import ipaddress

from networkapi.plugins.F5 import pool
from networkapi.plugins.F5 import rule
from networkapi.plugins.F5 import types
from networkapi.plugins.F5.f5base import F5Base
from networkapi.plugins.F5.profile import ProfileFastL4
from networkapi.plugins.F5.profile import ProfileHttp
from networkapi.plugins.F5.profile import ProfileTCP
from networkapi.plugins.F5.profile import ProfileUDP
from networkapi.plugins.F5.util import logger
from networkapi.util import valid_expression


log = logging.getLogger(__name__)


class VirtualServer(F5Base):

    __properties = None

    @logger
    def delete(self, **kwargs):
        self.__remove_all_rule(kwargs['vps_names'])
        self._lb._channel.LocalLB.VirtualServer.delete_virtual_server(
            virtual_servers=kwargs['vps_names']
        )

    @logger
    def create(self, **kwargs):
        self.__properties = {

            'profiles': list(),
            'profiles_timeout_tcp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_udp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_fastl4': {
                'profile_names': list(),
                'timeouts': list(),
                'loose_close_state': list(),
            },
            'profiles_persistence': {
                'virtual_servers': list(),
                'profiles': list()
            },
            'translate_port_state': {
                'virtual_servers': list(),
                'states': list()
            },
            'vip_snat_auto': {
                'virtual_servers': list()
            },
            'vip_snat_none': {
                'virtual_servers': list()
            },
            'vip_snat_pool': {
                'virtual_servers': list(),
                'pools': list()
            }
        }
        vip_definitions = list()
        vip_wildmasks = list()
        vip_resources = list()
        vip_profiles = list()

        vip_rules = {
            'virtual_servers': list(),
            'rules': list()
        }

        vip_traffic_groups = {
            'virtual_addresses': list(),
            'traffic_groups': list()
        }

        vip_dscp = {
            'pool_names': list(),
            'values': list()
        }

        rule_l7 = list()

        fastl4 = ProfileFastL4(self._lb)
        tcp = ProfileTCP(self._lb)
        http = ProfileHttp(self._lb)
        udp = ProfileUDP(self._lb)
        profiles_list = tcp.get_list() + http.get_list() + \
            fastl4.get_list() + udp.get_list()

        for vip_request in kwargs['vips']:

            self.__prepare_properties(vip_request, profiles_list)

            # VIP
            vip_definitions.append({
                'name': vip_request['name'],
                #IPv6 abbreviation - needed for traffic-group operations
                'address': str( ipaddress.ip_address( unicode(vip_request['address']) ) ),
                'port': vip_request['port'],
                'protocol': types.procotol_type(
                    vip_request['optionsvip']['l4_protocol']['nome_opcao_txt'].lower())
            })

            #Check VIP type and apply wildmask
            if ":" in vip_request['address']:
                vip_wildmasks.append('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
            else:
                vip_wildmasks.append('255.255.255.255')

            vip_resources.append({
                'type': 'RESOURCE_TYPE_POOL',
                'default_pool_name': vip_request.get('pool')
            })

            # DSCP
            if vip_request.get('optionsvip').get('dscp'):
                vip_dscp['values'].append(
                    vip_request['optionsvip']['dscp']['value'])
                vip_dscp['pool_names'].append(
                    vip_request['optionsvip']['dscp']['pool_name'])

            # TRAFFIC GROUP
            if vip_request.get('optionsvip').get('traffic_group'):
                vip_traffic_groups['virtual_addresses'].append(
                    vip_request['address'])
                vip_traffic_groups['traffic_groups'].append(
                    vip_request['optionsvip']['traffic_group'])

            # RULES
            rules = list()
            if vip_request.get('pool_l7'):
                rule_definition = vip_request['pool_l7']
                rule_name = '{}_RULE_L7'.format(vip_request['name'])

                rule_l7.append({
                    'rule_name': rule_name,
                    'rule_definition': rule_definition
                })

                rules.append({'rule_name': rule_name, 'priority': 1})

            if vip_request.get('rules'):
                for rule_name in vip_request['rules']:
                    rules.append({'rule_name': rule_name, 'priority': 1})

            if rules:
                vip_rules['rules'].append(rules)
                vip_rules['virtual_servers'].append(vip_request['name'])

        try:
            self._lb._channel.System.Session.start_transaction()

            if rule_l7:
                rl = rule.Rule(self._lb)
                rl.create(rules=rule_l7)

        except Exception, e:
            log.error(e)
            self._lb._channel.System.Session.rollback_transaction()
            raise e
        else:
            self._lb._channel.System.Session.submit_transaction()

            try:
                self._lb._channel.System.Session.start_transaction()

                self.__profiles_timeout_create(
                    profiles_timeout_tcp=self.__properties[
                        'profiles_timeout_tcp'],
                    profiles_timeout_udp=self.__properties[
                        'profiles_timeout_udp'],
                    profiles_timeout_fastl4=self.__properties['profiles_timeout_fastl4'])

                vip_profiles = self.__properties['profiles']
                self.__create_vip(
                    definitions=vip_definitions,
                    wildmasks=vip_wildmasks,
                    resources=vip_resources,
                    profiles=vip_profiles)

                self.__add_rule(vip_rules)

                self.__set_snat(
                    vip_snat_pool=self.__properties['vip_snat_pool'],
                    vip_snat_auto=self.__properties['vip_snat_auto'],
                    vip_snat_none=self.__properties['vip_snat_none'])

                self.__add_persistence_profile(
                    self.__properties['profiles_persistence'])

                self.__set_dscp(dscp=vip_dscp)

                self.__set_translate_port_state(
                    self.__properties['translate_port_state'])

            except Exception, e:
                log.error(e)
                self._lb._channel.System.Session.rollback_transaction()

                try:
                    rule.delete(rule_name=rule_l7['rule_name'])
                except Exception, e:
                    log.error(e)
                    raise e
                raise e

            else:
                self._lb._channel.System.Session.submit_transaction()

                if vip_traffic_groups['virtual_addresses']:
                    try:
                        self._lb._channel.System.Session.start_transaction()
                        self.__set_traffic_group(vip_traffic_groups)
                    except Exception, e:
                        log.error(e)
                        self._lb._channel.System.Session.rollback_transaction()
                        raise e
                    else:
                        self._lb._channel.System.Session.submit_transaction()

    @logger
    def partial_update(self, **kwargs):
        self.__properties = {

            'profiles': list(),
            'profiles_timeout_tcp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_udp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_fastl4': {
                'profile_names': list(),
                'timeouts': list(),
                'loose_close_state': list(),
            },
            'profiles_persistence': {
                'virtual_servers': list(),
                'profiles': list()
            },
            'translate_port_state': {
                'virtual_servers': list(),
                'states': list()
            },
            'vip_snat_auto': {
                'virtual_servers': list()
            },
            'vip_snat_none': {
                'virtual_servers': list()
            },
            'vip_snat_pool': {
                'virtual_servers': list(),
                'pools': list()
            }
        }

        virtual_servers = [vip_request['name']
                           for vip_request in kwargs['vips']]

        profiles_list = list()

        for idx, vip_request in enumerate(kwargs['vips']):

            self.__prepare_properties(vip_request, profiles_list)

        try:
            self._lb._channel.System.Session.start_transaction()

            self.__remove_all_persistence_profiles(virtual_servers)

            self.__add_persistence_profile(
                self.__properties['profiles_persistence'])

        except Exception, e:
            log.error(e)
            self._lb._channel.System.Session.rollback_transaction()
            raise e
        else:
            self._lb._channel.System.Session.submit_transaction()

    @logger
    def update(self, **kwargs):
        self.__properties = {

            'profiles': list(),
            'profiles_timeout_tcp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_udp': {
                'profile_names': list(),
                'timeouts': list()
            },
            'profiles_timeout_fastl4': {
                'profile_names': list(),
                'timeouts': list(),
                'loose_close_state': list(),
            },
            'profiles_persistence': {
                'virtual_servers': list(),
                'profiles': list()
            },
            'translate_port_state': {
                'virtual_servers': list(),
                'states': list()
            },
            'vip_snat_auto': {
                'virtual_servers': list()
            },
            'vip_snat_none': {
                'virtual_servers': list()
            },
            'vip_snat_pool': {
                'virtual_servers': list(),
                'pools': list()
            }
        }

        virtual_servers = [vip_request['name']
                           for vip_request in kwargs['vips']]

        resources = {
            'virtual_servers': list(),
            'pool': list()
        }

        vip_rules = {
            'virtual_servers': list(),
            'rules': list()
        }

        vip_dscp = {
            'pool_names': list(),
            'values': list()
        }

        rule_l7 = list()
        rule_l7_delete = list()

        profiles_list = list()

        for idx, vip_request in enumerate(kwargs['vips']):

            self.__prepare_properties(vip_request, profiles_list)

            # default pool
            resources['pool'].append(vip_request['pool'])
            resources['virtual_servers'].append(vip_request['name'])

            # DSCP
            vip_dscp['values'].append(
                vip_request['optionsvip']['dscp']['value'])
            vip_dscp['pool_names'].append(
                vip_request['optionsvip']['dscp']['pool_name'])

            # RULES

            rules = list()
            if vip_request.get('pool_l7'):
                rule_definition = vip_request['pool_l7']
                rule_name = '{}_RULE_L7'.format(vip_request['name'])

                rule_l7.append({
                    'rule_name': rule_name,
                    'rule_definition': rule_definition
                })

                rules.append({'rule_name': rule_name, 'priority': 1})
            else:
                rule_l7_delete.append('{}_RULE_L7'.format(vip_request['name']))

            if vip_request.get('rules'):
                for rule_name in vip_request['rules']:
                    rules.append({'rule_name': rule_name, 'priority': 1})

            if rules:
                vip_rules['rules'].append(rules)
                vip_rules['virtual_servers'].append(vip_request['name'])

        try:
            self._lb._channel.System.Session.start_transaction()

            if rule_l7:
                rl = rule.Rule(self._lb)
                rls = rl.list()
                for rl_l7 in rule_l7:
                    if '/Common/{}'.format(rl_l7['rule_name']) in rls:
                        rl.update(rules=[rl_l7])
                    else:
                        rl.create(rules=[rl_l7])

        except Exception, e:
            log.error(e)
            self._lb._channel.System.Session.rollback_transaction()
            raise e
        else:
            self._lb._channel.System.Session.submit_transaction()

            try:
                self._lb._channel.System.Session.start_transaction()

                self.__remove_all_persistence_profiles(virtual_servers)

                self.__remove_all_rule(virtual_servers)

                self.__add_rule(vip_rules)

                self.__set_default_pool_name(resources)

                self.__set_snat(
                    vip_snat_pool=self.__properties['vip_snat_pool'],
                    vip_snat_auto=self.__properties['vip_snat_auto'],
                    vip_snat_none=self.__properties['vip_snat_none'])

                self.__add_persistence_profile(
                    self.__properties['profiles_persistence'])

                self.__set_dscp(dscp=vip_dscp)

                self.__set_translate_port_state(
                    self.__properties['translate_port_state'])

            except Exception, e:
                log.error(e)
                self._lb._channel.System.Session.rollback_transaction()
                raise e
            else:
                self._lb._channel.System.Session.submit_transaction()

                if rule_l7_delete:
                    rl = rule.Rule(self._lb)
                    for rl_l7 in rule_l7_delete:
                        try:
                            rl.delete(rule_names=[rl_l7])
                        except Exception, e:
                            log.info(e)
                            pass

    @logger
    def __create_vip(self, **kwargs):
        self._lb._channel.LocalLB.VirtualServer.create(
            definitions=kwargs['definitions'],
            wildmasks=kwargs['wildmasks'],
            resources=kwargs['resources'],
            profiles=kwargs['profiles']
        )

    @logger
    def __set_snat(self, **kwargs):
        version_split = self._lb._version[8:len(self._lb._version)].split('.')
        # old version
        if version_split[0] == '11' and int(version_split[1]) <= 2:

            if kwargs.get('vip_snat_auto').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_snat_automap(
                    virtual_servers=kwargs.get(
                        'vip_snat_auto').get('virtual_servers')
                )
            if kwargs.get('vip_snat_pool').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_snat_pool(
                    virtual_servers=kwargs.get(
                        'vip_snat_pool').get('virtual_servers'),
                    pools=kwargs.get('vip_snat_pool').get('pools')
                )
            if kwargs.get('vip_snat_none').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_snat_none(
                    virtual_servers=kwargs.get(
                        'vip_snat_none').get('virtual_servers')
                )
        else:
            if kwargs.get('vip_snat_auto').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_source_address_translation_automap(
                    virtual_servers=kwargs.get(
                        'vip_snat_auto').get('virtual_servers')
                )
            if kwargs.get('vip_snat_pool').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_source_address_translation_snat_pool(
                    virtual_servers=kwargs.get(
                        'vip_snat_pool').get('virtual_servers'),
                    pools=kwargs.get('vip_snat_pool').get('pools')
                )
            if kwargs.get('vip_snat_none').get('virtual_servers'):
                self._lb._channel.LocalLB.VirtualServer.set_source_address_translation_none(
                    virtual_servers=kwargs.get(
                        'vip_snat_none').get('virtual_servers')
                )

    @logger
    def __add_persistence_profile(self, profiles_persistence):
        if profiles_persistence['virtual_servers']:
            self._lb._channel.LocalLB.VirtualServer.add_persistence_profile(
                virtual_servers=profiles_persistence['virtual_servers'],
                profiles=profiles_persistence['profiles']
            )

    @logger
    def __set_traffic_group(self, traffic_groups):
        if traffic_groups['virtual_addresses']:
            self._lb._channel.LocalLB.VirtualAddressV2.set_traffic_group(
                virtual_addresses=traffic_groups['virtual_addresses'],
                traffic_groups=traffic_groups['traffic_groups']
            )

    @logger
    def __set_translate_port_state(self, translate_port_state):
        if translate_port_state['virtual_servers']:
            self._lb._channel.LocalLB.VirtualServer.set_translate_port_state(
                virtual_servers=translate_port_state['virtual_servers'],
                states=translate_port_state['states']
            )

    @logger
    def __profiles_timeout_create(self, **kwargs):
        if kwargs['profiles_timeout_tcp']['profile_names']:
            ptcp = ProfileTCP(self._lb)
            ptcp.create(profile_names=kwargs[
                        'profiles_timeout_tcp']['profile_names'])
            ptcp.set_idle_timeout(
                profile_names=kwargs['profiles_timeout_tcp']['profile_names'],
                timeouts=kwargs['profiles_timeout_tcp']['timeouts'],
            )
        if kwargs['profiles_timeout_udp']['profile_names']:
            pudp = ProfileUDP(self._lb)
            pudp.create(profile_names=kwargs[
                        'profiles_timeout_udp']['profile_names'])
            pudp.set_idle_timeout(
                profile_names=kwargs['profiles_timeout_udp']['profile_names'],
                timeouts=kwargs['profiles_timeout_udp']['timeouts'],
            )
        if kwargs['profiles_timeout_fastl4']['profile_names']:
            pfl4 = ProfileFastL4(self._lb)
            pfl4.create(profile_names=kwargs[
                        'profiles_timeout_fastl4']['profile_names'])
            pfl4.set_idle_timeout(
                profile_names=kwargs[
                    'profiles_timeout_fastl4']['profile_names'],
                timeouts=kwargs['profiles_timeout_fastl4']['timeouts'],
            )
            if kwargs['profiles_timeout_fastl4']['loose_close_state']:
                pfl4.set_loose_close_state(
                    profile_names=kwargs[
                        'profiles_timeout_fastl4']['profile_names'],
                    states=kwargs['profiles_timeout_fastl4'][
                        'loose_close_state'],
                )

    @logger
    def __set_default_pool_name(self, resources):

        self._lb._channel.LocalLB.VirtualServer.set_default_pool_name(
            virtual_servers=resources['virtual_servers'],
            default_pools=resources['pool']
        )

    @logger
    def __add_rule(self, rules):
        if rules['virtual_servers']:
            self._lb._channel.LocalLB.VirtualServer.add_rule(
                virtual_servers=rules['virtual_servers'],
                rules=rules['rules']
            )

    @logger
    def __get_rule(self, virtual_servers):
        self._lb._channel.LocalLB.VirtualServer.get_rule(
            virtual_servers=virtual_servers
        )

    @logger
    def __remove_all_rule(self, virtual_servers):
        self._lb._channel.LocalLB.VirtualServer.remove_all_rules(
            virtual_servers=virtual_servers
        )

    @logger
    def __get_profile(self, virtual_servers):

        profiles = self._lb._channel.LocalLB.VirtualServer.get_profile(
            virtual_servers=virtual_servers
        )
        return profiles

    @logger
    def __remove_profile(self, profiles):
        if profiles['virtual_servers']:
            self._lb._channel.LocalLB.VirtualServer.remove_profile(
                virtual_servers=profiles['virtual_servers'],
                profiles=profiles['profiles']
            )

    @logger
    def __add_profile(self, profiles):
        if profiles['virtual_servers']:
            self._lb._channel.LocalLB.VirtualServer.add_profile(
                virtual_servers=profiles['virtual_servers'],
                profiles=profiles['profiles']
            )

    @logger
    def __set_protocol(self, protocols):
        self._lb._channel.LocalLB.VirtualServer.set_protocol(
            virtual_servers=protocols['virtual_servers'],
            protocols=protocols['protocols']
        )

    @logger
    def __remove_all_persistence_profiles(self, virtual_servers):

        self._lb._channel.LocalLB.VirtualServer.remove_all_persistence_profiles(
            virtual_servers=virtual_servers
        )

    @logger
    def __set_dscp(self, dscp):

        if dscp['values']:
            pl = pool.Pool(self._lb)
            pl.set_server_ip_tos(
                values=dscp['values'],
                pool_names=dscp['pool_names']
            )

    def __prepare_properties(self, vip_request, profiles_list, update=False):

        profiles = list()

        if vip_request['optionsvip_extended']:
            requiments = vip_request['optionsvip_extended'].get('requiments')
            if requiments:
                for requiment in requiments:
                    condicionals = requiment.get('condicionals')
                    for condicional in condicionals:

                        validated = True

                        validations = condicional.get('validations')
                        for validation in validations:
                            if validation.get('type') == 'optionvip':
                                validated &= valid_expression(
                                    validation.get('operator'),
                                    int(vip_request['optionsvip'][
                                        validation.get('variable')]['id']),
                                    int(validation.get('value'))
                                )

                        if validated:
                            use = condicional.get('use')
                            for item in use:
                                if item.get('type') == 'profile':
                                    profile_name = item.get('value')
                                    if '$' in profile_name:
                                        try:
                                            pn = profile_name.split('$')
                                            time_profile = int(vip_request['optionsvip'][
                                                               'timeout']['nome_opcao_txt']) * 60
                                            profile_name = pn[
                                                0] + str(time_profile)
                                            if '/Common/' + profile_name not in profiles_list:
                                                if 'tcp' in profile_name and profile_name not in self.__properties['profiles_timeout_tcp']['profile_names']:
                                                    self.__properties['profiles_timeout_tcp'][
                                                        'profile_names'].append(profile_name)
                                                    self.__properties['profiles_timeout_tcp']['timeouts'].append({
                                                        'value': time_profile,
                                                        'default_flag': 0
                                                    })
                                                elif 'udp' in profile_name and profile_name not in self.__properties['profiles_timeout_udp']['profile_names']:
                                                    self.__properties['profiles_timeout_udp'][
                                                        'profile_names'].append(profile_name)
                                                    self.__properties['profiles_timeout_udp']['timeouts'].append({
                                                        'value': time_profile,
                                                        'default_flag': 0
                                                    })
                                                elif 'fastL4' in profile_name and profile_name not in self.__properties['profiles_timeout_fastl4']['profile_names']:
                                                    self.__properties['profiles_timeout_fastl4'][
                                                        'profile_names'].append(profile_name)
                                                    self.__properties['profiles_timeout_fastl4']['timeouts'].append({
                                                        'value': time_profile,
                                                        'default_flag': 0
                                                    })
                                                    if 'fastL4_npath_' in profile_name:
                                                        self.__properties['profiles_timeout_fastl4']['loose_close_state'].append({
                                                            'value': 'STATE_ENABLED',
                                                            'default_flag': 0
                                                        })
                                        except:
                                            if '/Common/' + profile_name not in profiles_list:
                                                log.error(
                                                    u'Profile %s nao existe')
                                                raise Exception(
                                                    u'Profile %s nao existe')
                                            pass
                                    profiles.append({
                                        'profile_name': '/Common/' + profile_name,
                                        'profile_context': 'PROFILE_CONTEXT_TYPE_ALL'
                                    })

                                elif item.get('type') == 'snat':
                                    if item.get('value') == 'automap':
                                        self.__properties['vip_snat_auto'][
                                            'virtual_servers'].append(vip_request['name'])
                                    elif item.get('value') == '':
                                        self.__properties['vip_snat_none'][
                                            'virtual_servers'].append(vip_request['name'])
                                    else:
                                        self.__properties['vip_snat_pool'][
                                            'virtual_servers'].append(vip_request['name'])
                                        self.__properties['vip_snat_pool'][
                                            'pools'].append(item.get('value'))

                                elif item.get('type') == 'persistence':
                                    if item.get('value'):
                                        profile = [{
                                            'profile_name': item.get('value'),
                                            'default_profile': 1
                                        }]
                                        self.__properties['profiles_persistence'][
                                            'virtual_servers'].append(vip_request['name'])
                                        self.__properties['profiles_persistence'][
                                            'profiles'].append(profile)

                                elif item.get('type') == 'translate_port_state':
                                    self.__properties['translate_port_state'][
                                        'virtual_servers'].append(vip_request['name'])
                                    self.__properties['translate_port_state'][
                                        'states'].append(item.get('value'))

                            break
        self.__properties['profiles'].append(profiles)
