import base64, json, logging #, re
from flask import request, redirect, render_template, Blueprint, flash, session, url_for, current_app ,make_response
from common.proto.context_pb2 import ( Empty
,DeviceId ,OpticalConfig, OpticalConfigId ,OpticalConfigList)

from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from service.client.ServiceClient import ServiceClient
from slice.client.SliceClient import SliceClient
from .forms import UpdateDeviceForm ,AddTrancseiver ,UpdateInterfaceForm ,UpdateStatusForm
from common.tools.context_queries.OpticalConfig import opticalconfig_get_uuid , device_get_uuid


opticalconfig = Blueprint('opticalconfig', __name__,url_prefix="/opticalconfig")

context_client = ContextClient()
device_client = DeviceClient()
service_client = ServiceClient()
slice_client = SliceClient()

LOGGER = logging.getLogger(__name__)

DESCRIPTOR_LOADER_NUM_WORKERS = 10

@opticalconfig.get("/")
def home() :
    list_config=[]
    deviceId= DeviceId()
    channels_num = 0
    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()
    opticalConfig_list:OpticalConfigList = context_client.GetOpticalConfig(Empty())
   

    for configs in opticalConfig_list.opticalconfigs:

        value=json.loads(configs.config) if type(configs.config)==str else configs.config
        config_type = value["type"]
        if ('channels' in value):
           
           
            channels_num=len(value['channels']) 
            value["channels_number"]=channels_num
        # value['operationalMode']=value['operational-mode']
        # value['targetOutputPower']=value['target-output-power']
        value['opticalconfig_id']=configs.opticalconfig_id
        # value['line_port']=value["line-port"]
        
        list_config.append(value)
     
 
   
    context_client.close()

    return render_template(
        'opticalconfig/home.html', config=list_config)
    
    
    
@opticalconfig.route('<path:config_uuid>/detail',methods=['GET'])    
def show_details(config_uuid):
        opticalconfigId=OpticalConfigId()
        opticalconfigId.opticalconfig_uuid=config_uuid
        device_details=[]
       
        context_client.connect()
        response = context_client.SelectOpticalConfig(opticalconfigId)
        context_client.close()
        if (response and response.opticalconfig_id.opticalconfig_uuid !=''):
           
            opticalConfig = OpticalConfig()
            opticalConfig.CopyFrom(response)
               
            device_name=""
            config =json.loads(opticalConfig.config)
            if ("device_name" in config):
               device_name= config["device_name"]
            config_type = config["type"]
            
            if config_type == 'optical-transponder':
            
            

                if 'channels' in config:
           
                    for channel in config['channels'] :
                        new_config={}
                        new_config["name"]=channel['name']
                        new_config['operationalMode']=channel['operational-mode'] if 'operational-mode' in channel else ''
                        new_config['targetOutputPower']=channel['target-output-power'] if 'target-output-power' in channel else ''
                        new_config["frequency"]=channel['frequency'] if 'frequency' in channel else ''
                        new_config['line_port']=channel["line-port"] if 'line-port' in channel else ''
                        new_config["status"] = channel['status'] if 'status' in channel else ""
                    
                        device_details.append(new_config)
            
            if config_type == 'optical-roadm':
            
             
            

                if 'channels' in config:
           
                    for channel in config['channels'] :
                        new_config={}
                        new_config["band_name"]=channel['band_name'] if 'band_name' in channel else None
                        new_config['type']=channel['type'] if 'type' in channel else ''
                        new_config['src_port']=channel['src_port'] if 'src_port' in channel else ''
                        new_config['dest_port']=channel['dest_port'] if 'dest_port' in channel else ''
                        new_config["lower_frequency"]=channel['lower_frequency'] if 'lower_frequency' in channel else ''
                        new_config["upper_frequency"]=channel['upper_frequency'] if 'upper_frequency' in channel else ''
                        new_config["status"] = channel['status'] if 'status' in channel else ""
                        new_config['optical_band_parent']= channel['optical_band_parent'] if 'optical_band_parent' in channel else ''
                        new_config['channel_index']= channel['channel_index'] if 'channel_index' in channel else ''
                    
                        device_details.append(new_config)            
     
        
        return render_template('opticalconfig/details.html', device=device_details,config_id=config_uuid,device_name=device_name,type=config_type)



@opticalconfig.route('<path:opticalconfig_uuid>/delete', methods=['GET'])
def delete_opitcalconfig (opticalconfig_uuid)  :
    try : 
        opticalconfigId=OpticalConfigId()
        opticalconfigId.opticalconfig_uuid=opticalconfig_uuid
        context_client.connect()
        context_client.DeleteOpticalConfig(opticalconfigId)
        context_client.close()
        flash(f'OpticalConfig "{opticalconfig_uuid}" deleted successfully!', 'success')
    except Exception as e: # pylint: disable=broad-except
        flash(f'Problem deleting optical config {opticalconfig_uuid}', 'danger')
        current_app.logger.exception(e)   
    return redirect(url_for('opticalconfig.home'))     
    
    
@opticalconfig.route('/update_opticalconfig', methods=['POST'])
def update_externally ()  :
   
    if (request.method == 'POST'):
        device_list= []
        data = request.get_json()
  
        devices=data.get('devices')

        myResponse =[]
        status_code=''
        for device in devices :
            port = device.get("port")
            channel_name= f"channel-{port}"
            device_name=device.get("device_name")

        
            if (device_name):
                
                opticalconfig_uuid = opticalconfig_get_uuid(device_name=device_name)
                opticalconfigId=OpticalConfigId()
                opticalconfigId.opticalconfig_uuid=opticalconfig_uuid
                context_client.connect()
                opticalconfig = context_client.SelectOpticalConfig(opticalconfigId)
                context_client.close()
            
                if opticalconfig and opticalconfig.opticalconfig_id.opticalconfig_uuid != '' :
                
                    new_opticalconfig = OpticalConfig()
                    new_opticalconfig.CopyFrom(opticalconfig)
                    config =json.loads(opticalconfig.config)
                    channels= config['channels']
                    target_channel =next((item for item in channels if item["name"]['index'] == channel_name) , None)
                   
                    target_power=device.get( "target-output-power")
                    freq = device.get("frequency")
                    mode = device.get("operational-mode")
                    status= device.get("status","ENABLED")

                    if target_channel:
                        if target_power is not None :
                        
                            target_channel["target-output-power"] =str(target_power)
                        if freq  is not None :
                        
                            target_channel["frequency"] =freq
                        if mode is not None :
                        
                            target_channel["operational-mode"] =mode
                        if status is not None :
                            target_channel["status"]="ENABLED"    
                        #del target_channel['name']      
                        config["new_config"]=target_channel    
                        config["new_config"]["channel_name"]=channel_name
                        config["flow"]=[(port,'0')]
                        opticalconfig.config =json.dumps(config)

                        try:
                            device_client.connect()
                            device_client.ConfigureOpticalDevice(opticalconfig)
                            device_client.close()
                           
                            myResponse.append(f"device {device_name} port {port} is updated successfully")
                            status_code = 200
                        
                        
                        except Exception as e: # pylint: disable=broad-except
                          
                            myResponse.append(f"Problem updating the device. {e}")
                            status_code = 500 
                            break   
                        
                    else :
                       
                        myResponse.append(f"requested channel {channel_name} is not existed")
                        status_code = 400
                        break         
                    
                else :
                    
                    myResponse.append(f"requested device {device_name} is not existed")
                    status_code = 400
                    break              
            
       

        response=make_response(f'{myResponse}')
        response.status_code=status_code
        
        return response
        

        #return redirect(url_for('opticalconfig.show_details',config_uuid=opticalconfig_uuid))
        #return redirect(url_for('opticalconfig.home'))
    
@opticalconfig.route('<path:config_uuid>/<path:channel_name>/update', methods=['GET', 'POST'])
def update(config_uuid,channel_name):
    
    form = UpdateDeviceForm()

    opticalconfigId=OpticalConfigId()
    opticalconfigId.opticalconfig_uuid=config_uuid
    context_client.connect()
    response = context_client.SelectOpticalConfig(opticalconfigId)
    context_client.close()

    opticalconfig = OpticalConfig()
    opticalconfig.CopyFrom(response)
    config =json.loads(opticalconfig.config)
    new_config={}
    for channel in config['channels']:
        if (channel["name"] == channel_name):
            new_config=channel
            form.frequency.default = channel["frequency"]
            form.operational_mode.default=channel["operational-mode"]
            form.power.default=channel["target-output-power"]
            form.line_port.choices = [("","")]
            
    for transceiver in config["transceivers"]['transceiver']:
        
        form.line_port.choices.append((transceiver,transceiver))
    # listing enum values

    if form.validate_on_submit():
       
        new_config["target-output-power"] =form.power.data if form.power.data != '' else  new_config['target-output-power']
        new_config["frequency"]=form.frequency.data if form.frequency.data != '' else new_config['frequency']
        new_config["operational-mode"]=form.operational_mode.data if form.operational_mode.data != '' else new_config['operational-mode']
        new_config["line-port"]=form.line_port.data if form.line_port.data != '' else new_config['line-port']

        opticalconfig.config =json.dumps(new_config)
 
        try:
            device_client.connect()
            device_client.ConfigureOpticalDevice(opticalconfig)
            device_client.close()
            flash(f' device  was updated.', 'success')
            return redirect(url_for('opticalconfig.show_details',config_uuid=config_uuid))
        except Exception as e: # pylint: disable=broad-except
             flash(f'Problem updating the device. {e}', 'danger')  
    return render_template('myabout/update.html', device=response, form=form, submit_text='Update Device',channel_name=channel_name)


@opticalconfig.route('refresh_all',methods=['POST','GET'])
def refresh_all ():
    context_client.connect()
    opticalConfig_list:OpticalConfigList = context_client.GetOpticalConfig(Empty())
    context_client.close()
    device_client.connect()
    device_client.GetDeviceConfiguration(opticalConfig_list)
    device_client.close()
    return home()
    
@opticalconfig.route('<path:config_uuid>/add_transceiver', methods=['GET','POST'])
def add_transceiver (config_uuid):
    config={}
    addtrancseiver=AddTrancseiver()
    opticalconfigId=OpticalConfig()
    opticalconfigId.opticalconfig_uuid=config_uuid
    context_client.connect()
    response = context_client.SelectOpticalConfig(opticalconfigId)
    context_client.close()
    opticlConfig=OpticalConfig()
    opticlConfig.CopyFrom(response)
   
    if addtrancseiver.validate_on_submit():
        config["add_transceiver"]=addtrancseiver.transceiver.data
        opticlConfig.config=json.dumps(config)
       
        try:
            device_client.connect()
            device_client.ConfigureOpticalDevice(opticlConfig)
            device_client.close()
            flash(f' device  was updated.', 'success')
            return redirect(url_for('opticalconfig.update',config_uuid=config_uuid))
        except Exception as e: # pylint: disable=broad-except
             flash(f'Problem updating the device. {e}', 'danger')  
    return render_template('opticalconfig/add_transceiver.html',form=addtrancseiver, submit_text='Add Trancseiver')         
 
        

@opticalconfig.route('<path:config_uuid>/<path:channel_name>/update_status', methods=['GET','POST'])
def update_status (config_uuid,channel_name):
    
    config={}
    form=UpdateStatusForm()

    opticalconfigId=OpticalConfigId()
    opticalconfigId.opticalconfig_uuid=config_uuid
    context_client.connect()
    response = context_client.SelectOpticalConfig(opticalconfigId)
    context_client.close()
    opticlConfig=OpticalConfig()
    opticlConfig.CopyFrom(response)
    config=json.loads(opticlConfig.config)
    new_config={}
    port=""
    if form.validate_on_submit():
        if channel_name:
            port = channel_name.split('-')[1]
        new_config["status"]=form.status.data
        new_config["name"]=channel_name
        config["flow"]=[(port,'0')]
        config["new_config"]=new_config
        opticlConfig.config=json.dumps(config)
       
        try:
      
            device_client.connect()
            device_client.ConfigureOpticalDevice(opticlConfig)
            device_client.close()
            flash(f' device  was updated.', 'success')
            return redirect(url_for('opticalconfig.show_details',config_uuid=config_uuid))
        except Exception as e: # pylint: disable=broad-except
             flash(f'Problem updating the device. {e}', 'danger')  
    return render_template('opticalconfig/update_status.html',form=form , channel_name=channel_name, submit_text='Update Device Status')         
 
        
               
    