#!{{ python_path }}
import logging
import time
import xmltodict
from logging.handlers import RotatingFileHandler
from ncclient import manager
from ncclient.operations import RPCError
from ncclient.operations.rpc import RPC
from ncclient.xml_ import *
from ncclient.devices.default import DefaultDeviceHandler


class LopcommNetconfClient:

    def __init__(self):

        log_file = "{{ log_file }}"
        software_output_json_log_file = "{{ software_output_json_log_file }}"

        self.logger = logging.getLogger('logger')
        self.software_output_json_logger = logging.getLogger('software_output_json_logger')
        self.logger.setLevel(logging.DEBUG)
        self.software_output_json_logger.setLevel(logging.DEBUG)

        handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=5)
        self.logger.addHandler(handler)
        formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
        handler.setFormatter(formatter)

        software_output_json_handler = RotatingFileHandler(software_output_json_log_file, maxBytes=100000, backupCount=5)
        software_output_json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
        software_output_json_handler.setFormatter(software_output_json_formatter)
        self.software_output_json_logger.addHandler(software_output_json_handler)

        if {{ testing }}:
            return

    def connect(self, host, port, user, password):

        if {{ testing }}:
            return

        self.address = (host, port)

        self.logger.info('Connecting to %s, user %s...' % (self.address, user))

        self.conn = manager.connect(host=host,
                               port=port,
                               username=user,
                               password=password,
                               timeout=1800,
                               device_params={
                                   'name': 'default'
                               },
                               hostkey_verify=False)

        self.logger.info('Connection to %s successful' % (self.address,))
    def custom_rpc_request(self, rpc_xml):
        try:
            self.logger.info('Sending custom RPC request...')
            response = self.conn.dispatch(to_ele(rpc_xml))
            if response.ok:
                self.logger.info('Custom RPC request sent successfully')
                return response.xml
            else:
                self.logger.error('Error sending custom RPC request: %s' % response.error)
        except RPCError as e:
            self.logger.error('Error sending custom RPC request: %s' % e)

    def close(self):
        # Close not compatible between ncclient and netconf server
        #self.conn.close()
        pass
if __name__ == '__main__':
    nc = LopcommNetconfClient()
    while True:
        try:
            #nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword")
            nc.connect("fe80::20a:ff:fe00:1020%slaptap6", 830, "oranuser", "oranpassword")
            # Fetch software inventory
            inventory_rpc_xml = """
                <get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
                    <filter type="subtree">
                        <software-inventory xmlns="urn:o-ran:software-management:1.0" />
                    </filter>
                </get>
            """
            inventory_reply_xml = nc.custom_rpc_request(inventory_rpc_xml)

            if inventory_reply_xml:
                inventory_data = xmltodict.parse(inventory_reply_xml)
                nc.software_output_json_logger.info('', extra={'data': inventory_data})


                # Find active software slot
                active_slot_name = None
                nonactive_slot_name = None
                software_slots = inventory_data['nc:rpc-reply']['data']['software-inventory']['software-slot']
                for slot in software_slots:
                    if slot['active'] == 'true' and slot['running'] == 'true':
                        active_slot_name = slot['name']
                        active_slot_build_version = slot['build-version']  # Extract the build-version of the active slot
                    elif slot['active'] == 'false' and slot['running'] == 'false':
                        nonactive_slot_name = slot['name']

                if active_slot_name and nonactive_slot_name:
                    # Check if the running slot's build-version is already in file-names of the active slot
                    if active_slot_build_version in "{{ firmware_name }}":
                        nc.logger.info("Running slot's build-version is already updated. Skipping install.")
                    else:
                        nc.logger.info("Currrent build version: %s" % active_slot_build_version)
                        # Install software in the non-active slot
                        install_rpc_xml = f"""
                            <software-install xmlns="urn:o-ran:software-management:1.0">
                                <slot-name>{nonactive_slot_name}</slot-name>
                                <file-names>{{ firmware_name }}</file-names>
                            </software-install>
                        """
                        install_reply_xml = nc.custom_rpc_request(install_rpc_xml)

                        if install_reply_xml:
                            install_data = xmltodict.parse(install_reply_xml)
                            nc.software_output_json_logger.info('', extra={'data': install_data})
            break

        except Exception as e:
            nc.logger.debug('Got exception, waiting 10 seconds before reconnecting...')
            nc.logger.debug(str(e))
            time.sleep(10)
        finally:
            nc.close()
