# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json, logging
from typing import Any, Dict, List, Tuple
from ._Handler import _Handler
from .Tools import dict_get_first

LOGGER = logging.getLogger(__name__)

class InterfaceHandler(_Handler):
    def get_resource_key(self) -> str: return '/interface'
    def get_path(self) -> str: return '/interfaces/interface'

    def compose(self, resource_key : str, resource_value : Dict, delete : bool = False) -> Tuple[str, str]:
        if_name          = str (resource_value['name'                         ])    # ethernet-1/1
        sif_index        = int (resource_value.get('sub_if_index'       , 0   ))    # 0

        if delete:
            PATH_TMPL = '/interfaces/interface[name={:s}]/subinterfaces/subinterface[index={:d}]'
            str_path = PATH_TMPL.format(if_name, sif_index)
            str_data = json.dumps({})
            return str_path, str_data

        if_enabled       = bool(resource_value.get('enabled'            , True))    # True/False
        sif_enabled      = bool(resource_value.get('sub_if_enabled'     , True))    # True/False
        sif_ipv4_enabled = bool(resource_value.get('sub_if_ipv4_enabled', True))    # True/False
        sif_ipv4_address = str (resource_value['sub_if_ipv4_address'          ])    # 172.16.0.1
        sif_ipv4_prefix  = int (resource_value['sub_if_ipv4_prefix'           ])    # 24

        str_path = '/interfaces/interface[name={:s}]'.format(if_name)
        str_data = json.dumps({
            'name': if_name,
            'config': {'name': if_name, 'enabled': if_enabled},
            'subinterfaces': {
                'subinterface': {
                    'index': sif_index,
                    'config': {'index': sif_index, 'enabled': sif_enabled},
                    'ipv4': {
                        'config': {'enabled': sif_ipv4_enabled},
                        'addresses': {
                            'address': {
                                'ip': sif_ipv4_address,
                                'config': {'ip': sif_ipv4_address, 'prefix_length': sif_ipv4_prefix},
                            }
                        }
                    }
                }
            }
        })
        return str_path, str_data

    def parse(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
        #LOGGER.info('json_data = {:s}'.format(json.dumps(json_data)))
        json_interface_list : List[Dict] = json_data.get('interface', [])

        response = []
        for json_interface in json_interface_list:
            #LOGGER.info('json_interface = {:s}'.format(json.dumps(json_interface)))

            interface = {}

            interface_name = json_interface.get('name')
            if interface_name is None:
                LOGGER.info('DISCARDED json_interface = {:s}'.format(json.dumps(json_interface)))
                continue
            interface['name'] = interface_name

            CONFIG_FIELDS = ('config', 'openconfig-interface:config', 'oci:config')
            json_config : Dict = dict_get_first(json_interface, CONFIG_FIELDS, default={})

            STATE_FIELDS = ('state', 'openconfig-interface:state', 'oci:state')
            json_state : Dict = dict_get_first(json_interface, STATE_FIELDS, default={})

            interface_type = json_config.get('type')
            if interface_type is None: interface_type = json_state.get('type')
            if interface_type is None:
                LOGGER.info('DISCARDED json_interface = {:s}'.format(json.dumps(json_interface)))
                continue
            interface_type = interface_type.replace('ianaift:', '')
            interface_type = interface_type.replace('iana-if-type:', '')
            interface['type'] = interface_type

            interface_mtu = json_config.get('mtu')
            if interface_mtu is None: interface_mtu = json_state.get('mtu')
            if interface_mtu is not None: interface['mtu'] = int(interface_mtu)

            interface_enabled = json_config.get('enabled')
            if interface_enabled is None: interface_enabled = json_state.get('enabled')
            interface['enabled'] = False if interface_enabled is None else bool(interface_enabled)

            interface_management = json_config.get('management')
            if interface_management is None: interface_management = json_state.get('management')
            interface['management'] = False if interface_management is None else bool(interface_management)

            interface_descr = json_interface.get('config', {}).get('description')
            if interface_descr is not None: interface['description'] = interface_descr

            json_subinterfaces = json_interface.get('subinterfaces', {})
            json_subinterface_list : List[Dict] = json_subinterfaces.get('subinterface', [])

            for json_subinterface in json_subinterface_list:
                #LOGGER.info('json_subinterface = {:s}'.format(json.dumps(json_subinterface)))

                subinterface = {}

                subinterface_index = json_subinterface.get('state', {}).get('index')
                if subinterface_index is None: continue
                subinterface['index'] = int(subinterface_index)

                subinterface_name = json_subinterface.get('state', {}).get('name')
                if subinterface_name is None: continue
                subinterface['name'] = subinterface_name

                subinterface_enabled = json_subinterface.get('state', {}).get('enabled', False)
                subinterface['enabled'] = bool(subinterface_enabled)

                VLAN_FIELDS = ('vlan', 'openconfig-vlan:vlan', 'ocv:vlan')
                json_vlan = dict_get_first(json_subinterface, VLAN_FIELDS, default={})

                MATCH_FIELDS = ('match', 'openconfig-vlan:match', 'ocv:match')
                json_vlan = dict_get_first(json_vlan, MATCH_FIELDS, default={})

                SIN_TAG_FIELDS = ('single-tagged', 'openconfig-vlan:single-tagged', 'ocv:single-tagged')
                json_vlan = dict_get_first(json_vlan, SIN_TAG_FIELDS, default={})

                CONFIG_FIELDS = ('config', 'openconfig-vlan:config', 'ocv:config')
                json_vlan = dict_get_first(json_vlan, CONFIG_FIELDS, default={})

                VLAN_ID_FIELDS = ('vlan-id', 'openconfig-vlan:vlan-id', 'ocv:vlan-id')
                subinterface_vlan_id = dict_get_first(json_vlan, VLAN_ID_FIELDS)
                if subinterface_vlan_id is not None: subinterface['vlan_id'] = subinterface_vlan_id


                # TODO: implement support for multiple IP addresses per subinterface

                IPV4_FIELDS = ('ipv4', 'openconfig-if-ip:ipv4', 'ociip:ipv4')
                json_ipv4 = dict_get_first(json_subinterface, IPV4_FIELDS, default={})
                
                IPV4_ADDRESSES_FIELDS = ('addresses', 'openconfig-if-ip:addresses', 'ociip:addresses')
                json_ipv4_addresses = dict_get_first(json_ipv4, IPV4_ADDRESSES_FIELDS, default={})

                IPV4_ADDRESS_FIELDS = ('address', 'openconfig-if-ip:address', 'ociip:address')
                json_ipv4_address_list : List[Dict] = dict_get_first(json_ipv4_addresses, IPV4_ADDRESS_FIELDS, default=[])

                #ipv4_addresses = []
                for json_ipv4_address in json_ipv4_address_list:
                    #LOGGER.info('json_ipv4_address = {:s}'.format(json.dumps(json_ipv4_address)))

                    STATE_FIELDS = ('state', 'openconfig-if-ip:state', 'ociip:state')
                    json_ipv4_address_state = dict_get_first(json_ipv4_address, STATE_FIELDS, default={})

                    #ipv4_address = {}

                    #ORIGIN_FIELDS = ('origin', 'openconfig-if-ip:origin', 'ociip:origin')
                    #ipv4_address_origin = dict_get_first(json_ipv4_address_state, ORIGIN_FIELDS, default={})
                    #if ipv4_address_origin is not None: ipv4_address['origin'] = ipv4_address_origin

                    IP_FIELDS = ('ip', 'openconfig-if-ip:ip', 'ociip:ip')
                    ipv4_address_ip = dict_get_first(json_ipv4_address_state, IP_FIELDS)
                    #if ipv4_address_ip is not None: ipv4_address['address_ip'] = ipv4_address_ip
                    if ipv4_address_ip is not None: subinterface['address_ip'] = ipv4_address_ip

                    PREFIX_FIELDS = ('prefix-length', 'openconfig-if-ip:prefix-length', 'ociip:prefix-length')
                    ipv4_address_prefix = dict_get_first(json_ipv4_address_state, PREFIX_FIELDS)
                    #if ipv4_address_prefix is not None: ipv4_address['address_prefix'] = int(ipv4_address_prefix)
                    if ipv4_address_prefix is not None: subinterface['address_prefix'] = int(ipv4_address_prefix)

                    #if len(ipv4_address) == 0: continue
                    #ipv4_addresses.append(ipv4_address)

                #subinterface['ipv4_addresses'] = ipv4_addresses

                if len(subinterface) == 0: continue
                resource_key = '/interface[{:s}]/subinterface[{:s}]'.format(interface['name'], str(subinterface['index']))
                response.append((resource_key, subinterface))

            if len(interface) == 0: continue
            response.append(('/interface[{:s}]'.format(interface['name']), interface))

        return response

    def parse_counters(self, json_data : Dict) -> List[Tuple[str, Dict[str, Any]]]:
        LOGGER.info('[parse_counters] json_data = {:s}'.format(json.dumps(json_data)))
        json_interface_list : List[Dict] = json_data.get('interface', [])

        response = []
        for json_interface in json_interface_list:
            LOGGER.info('[parse_counters] json_interface = {:s}'.format(json.dumps(json_interface)))

            interface = {}

            NAME_FIELDS = ('name', 'openconfig-interface:name', 'oci:name')
            interface_name = dict_get_first(json_interface, NAME_FIELDS)
            if interface_name is None: continue
            interface['name'] = interface_name

            STATE_FIELDS = ('state', 'openconfig-interface:state', 'oci:state')
            json_state = dict_get_first(json_interface, STATE_FIELDS, default={})

            COUNTERS_FIELDS = ('counters', 'openconfig-interface:counters', 'oci:counters')
            json_counters = dict_get_first(json_state, COUNTERS_FIELDS, default={})

            IN_PKTS_FIELDS = ('in-pkts', 'openconfig-interface:in-pkts', 'oci:in-pkts')
            interface_in_pkts = dict_get_first(json_counters, IN_PKTS_FIELDS)
            if interface_in_pkts is not None: interface['in-pkts'] = int(interface_in_pkts)

            IN_OCTETS_FIELDS = ('in-octets', 'openconfig-interface:in-octets', 'oci:in-octets')
            interface_in_octets = dict_get_first(json_counters, IN_OCTETS_FIELDS)
            if interface_in_octets is not None: interface['in-octets'] = int(interface_in_octets)

            IN_ERRORS_FIELDS = ('in-errors', 'openconfig-interface:in-errors', 'oci:in-errors')
            interface_in_errors = dict_get_first(json_counters, IN_ERRORS_FIELDS)
            if interface_in_errors is not None: interface['in-errors'] = int(interface_in_errors)

            OUT_OCTETS_FIELDS = ('out-octets', 'openconfig-interface:out-octets', 'oci:out-octets')
            interface_out_octets = dict_get_first(json_counters, OUT_OCTETS_FIELDS)
            if interface_out_octets is not None: interface['out-octets'] = int(interface_out_octets)

            OUT_PKTS_FIELDS = ('out-pkts', 'openconfig-interface:out-pkts', 'oci:out-pkts')
            interface_out_pkts = dict_get_first(json_counters, OUT_PKTS_FIELDS)
            if interface_out_pkts is not None: interface['out-pkts'] = int(interface_out_pkts)

            OUT_ERRORS_FIELDS = ('out-errors', 'openconfig-interface:out-errors', 'oci:out-errors')
            interface_out_errors = dict_get_first(json_counters, OUT_ERRORS_FIELDS)
            if interface_out_errors is not None: interface['out-errors'] = int(interface_out_errors)

            OUT_DISCARDS_FIELDS = ('out-discards', 'openconfig-interface:out-discards', 'oci:out-discards')
            interface_out_discards = dict_get_first(json_counters, OUT_DISCARDS_FIELDS)
            if interface_out_discards is not None: interface['out-discards'] = int(interface_out_discards)

            #LOGGER.info('[parse_counters] interface = {:s}'.format(str(interface)))

            if len(interface) == 0: continue
            response.append(('/interface[{:s}]'.format(interface['name']), interface))

        return response
