# 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 flask import render_template, Blueprint, flash, session, redirect, url_for, request
from common.proto.context_pb2 import (
    ConfigActionEnum, Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum, 
    Empty, TopologyId)
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Topology import json_topology_id
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from webui.service.pcep.forms import ConfigIpPCEForm, SendPathForm

from pcep.client.PcepClient import PcepClient
#from pcep.service.tools.Grpc_TestClient.gRPC_PCEPClient import GrpcPcepClient

from webui.service.device.forms import AddDeviceForm
from common.DeviceTypes import DeviceTypeEnum

from common.proto.pcep_pb2 import (PceIpRq, RequestRq)

pcep = Blueprint('pcep', __name__, url_prefix='/pcep')
context_client = ContextClient()
device_client = DeviceClient()
pcep_client = PcepClient()
#grpc_pcep_client = GrpcPcepClient()
logger = logging.getLogger(__name__)

@pcep.get('/')
def home():
    if 'context_uuid' not in session or 'topology_uuid' not in session:
        flash("Please select a context!", "warning")
        return redirect(url_for("main.home"))

    context_uuid = session['context_uuid']
    topology_uuid = session['topology_uuid']

    context_client.connect()
    json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid))
    grpc_topology = context_client.GetTopology(TopologyId(**json_topo_id))
    topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
    
    if grpc_topology is None:
        flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger')
        devices = []
    else:
        topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
        grpc_devices: DeviceList = context_client.ListDevices(Empty())
        devices = [
            device for device in grpc_devices.devices
            if device.device_id.device_uuid.uuid in topo_device_uuids
        ]

    # ListNewDevices discovered from bgpls
    logger.info('pcep/home')
    pcep_client.connect()
    logger.info('pcep_client.connect %s',pcep_client)
    
    context_client.close()
    pcep_client.close()

    return render_template(
        'pcep/home.html', devices=devices, dde=DeviceDriverEnum,
        dose=DeviceOperationalStatusEnum)

@pcep.route('managePCEP', methods=['GET', 'POST'])
def managePCEP():
    pcep_manage_form = SendPathForm()
    if pcep_manage_form.validate_on_submit():
        pcep_client.connect()
        logger.info('Send Path ip:%s',pcep_manage_form.command.data)
        command_response = pcep_client.sendRequest(RequestRq(command=pcep_manage_form.command.data))
        #logger.info('THIS IS THE RESPONSE:%s',command_response)
        #logger.info('THIS IS THE RESPONSE BOOL:%s',command_response.success)
        #logger.info('THIS IS THE RESPONSE ERROR:%s',command_response.error_message)
        if command_response.success:
            flash(f'Command: "{pcep_manage_form.command.data}" was executed sucesfully!', 'success')
        else:
            flash(f'Command "{pcep_manage_form.command.data}" was not executed sucesfully', 'danger')
            flash(f'Error Information: "{command_response.error_message}"', 'warning')
        pcep_client.close()
    
    pcep_client.connect()
    sessions = pcep_client.showSessions()
    pcep_client.close()

    return render_template('pcep/managePCEP.html', pcep_manage_form=pcep_manage_form, sessions=sessions)

@pcep.route('lspdbPCEP', methods=['GET', 'POST'])
def lspdbPCEP():
    pcep_client.connect()
    logger.info('pcep_client.connect %s',pcep_client)

    lspdb = pcep_client.showLSPDB()
    if lspdb:
        flash(f'LSPDB data retrieved', 'success')
    pcep_client.close()
    return render_template('pcep/lspdb.html', lspdb=lspdb)

@pcep.route('sendPath', methods=['GET', 'POST'])
def sendPath():
    pcep_client.connect()
    form = SendPathForm()
    response = None
    if form.validate_on_submit():
        logger.info('Send Path ip:%s',form.command.data)
        response = pcep_client.sendRequest(RequestRq(command=form.command.data))
        flash(f'Command "{form.command.data}" added successfully!', 'success')
    pcep_client.close()
    return render_template('pcep/sendPath.html',form=form,response=response)


@pcep.route('add/<path:device_name>', methods=['GET', 'POST'])
def add(device_name):
    """"
    Add a discovered device from bgpls protocol. Populate form from
    existent info in bgpls. 
    """
    # TODO: Conect to device and get necessary info
    form = AddDeviceForm()

    logger.info('pcep/add')    
    
    # listing enum values
    form.operational_status.choices = []
    for key, _ in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items():
        form.operational_status.choices.append(
            (DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', '')))
        
    form.device_type.choices = []
    # items for Device Type field
    for device_type in DeviceTypeEnum:
        form.device_type.choices.append((device_type.value,device_type.value))    

    if form.validate_on_submit():
        device_obj = Device()
        # Device UUID: 
        device_obj.device_id.device_uuid.uuid = form.device_id.data # pylint: disable=no-member

        # Device type: 
        device_obj.device_type = str(form.device_type.data)

        # Device configurations: 
        config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member
        config_rule.action = ConfigActionEnum.CONFIGACTION_SET
        config_rule.custom.resource_key = '_connect/address'
        config_rule.custom.resource_value = form.device_config_address.data

        config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member
        config_rule.action = ConfigActionEnum.CONFIGACTION_SET
        config_rule.custom.resource_key = '_connect/port'
        config_rule.custom.resource_value = form.device_config_port.data

        config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member
        config_rule.action = ConfigActionEnum.CONFIGACTION_SET
        config_rule.custom.resource_key = '_connect/settings'

        try:
            device_config_settings = json.loads(form.device_config_settings.data)
        except: # pylint: disable=bare-except
            device_config_settings = form.device_config_settings.data

        if isinstance(device_config_settings, dict):
            config_rule.custom.resource_value = json.dumps(device_config_settings)
        else:
            config_rule.custom.resource_value = str(device_config_settings)

        # Device status: 
        device_obj.device_operational_status = form.operational_status.data

        # Device drivers: 
        if form.device_drivers_undefined.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_UNDEFINED)
        if form.device_drivers_openconfig.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG)
        if form.device_drivers_transport_api.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API)
        if form.device_drivers_p4.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_P4)
        if form.device_drivers_ietf_network_topology.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY)
        if form.device_drivers_onf_tr_352.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352)
        if form.device_drivers_xr.data:
            device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR)

        try:
            device_client.connect()
            logger.info('add device from pcep:%s',device_obj)
            response: DeviceId = device_client.AddDevice(device_obj)
            device_client.close()
            flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success')
            #pcep_client.connect()
            #pcep_client.configuratePCE(PceIpRq(address=device_obj.device_id.device_uuid.uuid))
            #pcep_client.close()
            return redirect(url_for('device.home'))
        except Exception as e:
            flash(f'Problem adding the device. {e.details()}', 'danger')
        
    # Prefill data with discovered info from speaker
    # Device Name from bgpls
    form.device_name=device_name
    device=device_name
    form.device_id.data=device_name
    # Default values (TODO: NOT WORKING)
    form.device_type.data=DeviceTypeEnum.EMULATED_PACKET_ROUTER
    form.device_config_settings.data=str('{"username": "admin", "password": "admin"}')

    return render_template('pcep/add.html', form=form, device=device,
                        submit_text='Add New Device')

@pcep.route('detail/<path:device_uuid>', methods=['GET', 'POST'])
def detail(device_uuid: str):
    request = DeviceId()
    request.device_uuid.uuid = device_uuid
    context_client.connect()
    response = context_client.GetDevice(request)
    context_client.close()
    return render_template('pcep/detail.html', device=response,
                                                 dde=DeviceDriverEnum,
                                                 dose=DeviceOperationalStatusEnum)

@pcep.route('addPcep', methods=['GET', 'POST'])
def addPcep():

    pcep_client.connect()
    form = ConfigIpPCEForm()
    if form.validate_on_submit():
        pcep_client.connect()
        logger.info('addPcep ip:%s',form.pce_address.data)
        pcep_client.configuratePCE(PceIpRq(address=form.pce_address.data))
        logger.info('Prueba 1')
        flash(f'Pcep "{form.pce_address.data}" added successfully!', 'success')
        logger.info('Prueba 2')
        pcep_client.close()
    logger.info('Prueba 3')
    return render_template('pcep/addPcep.html',form=form)




@pcep.route('formPcep', methods=['GET','POST'])
def formPcep():
    #conectar con pcep?
    form = ConfigIpPCEForm()
    if request.method=="POST":
        address = form.pce_address.data
        logger.info("FORM formPcep: %s ", address)
        
        flash(f'Pcep "{address}" added successfully!', 'success')

    return redirect(url_for('pcep.home'))

