'''
Created on Mar 15, 2021

@author: Saurabh Kumar Pandey

This scipt will pick its configuration from the config.ini
This worker will split the data to multiple queues to delhi and pune

!!!!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!
Create_User_Xchange -> Create_NonDemo_User_PROD  -> Create_Account_Xchange    -> Create_Account_DELHI
                                                                            -> Create_Account_PUNE
Create_User_Xchange -> Set_User_Pin_PROD  -> Set_User_Pin_Xchange            -> Set_User_Pin_DELHI
                                                                            -> Set_User_Pin_PUNE
Create_User_Xchange -> Update_User_PROD   -> Update_User_Xchange            -> Update_User_DELHI
                                                                            -> Update_User_PUNE
Create_User_Xchange -> Update_User_Profile_PROD  -> Update_User_Profile_Xchange    -> Update_User_Profile_DELHI
                                                                            -> Update_User_Profile_PUNE
Create_User_Xchange -> User_Attempts_Log_PROD  -> User_Attempts_Log_Xchange    -> User_Attempts_Log_DELHI
                                                                            -> User_Attempts_Log_PUNE
Create_User_Xchange -> User_Creation_Log_PROD  -> User_Creation_Log_Xchange    -> User_Creation_Log_DELHI
                                                                            -> User_Creation_Log_PUNE                                                                            
Create_User_Xchange -> User_Address_PROD  -> User_Address_Xchange            -> User_Address_DELHI
                                                                            -> User_Address_PUNE
Create_User_Xchange -> Convert_User_PROD  -> Convert_User_Xchange            -> Convert_User_DELHI
                                                                            -> Convert_User_PUNE                                                                            

'''
#Standard library import
#this will pull the demographic details from Queue and publish it to the MYSQL or mongodb user linking
import pika, json , datetime , hashlib
from configparser import ConfigParser
from lib.log import logger
from lib.user_type_validation import validation
from datetime import date, timedelta
 
config_object = ConfigParser()
config_object.read("/opt/rmq-workers/config.ini")
rabbitmq = config_object["rabbitmq"]
application_environment = config_object["environment"]['application']
application_location = config_object["environment"]['location']
prefetch_counter = config_object["prefetch_count"]

#RabbitMQ connection credentials
try:
    credentials = pika.PlainCredentials(rabbitmq['username'], rabbitmq['password'])
    parameters = pika.ConnectionParameters(rabbitmq['host'], rabbitmq['port'], rabbitmq['vhost'], credentials, socket_timeout=int(rabbitmq['socket_timeout']))                                   
    
    #Connection to a broker on the local machine
    connection = pika.BlockingConnection(parameters)
    channel = connection.channel()

    exchange_name = 'Create_User_Xchange'

    #channel.exchange_declare(exchange=exchange_name, exchange_type='fanout', durable=True)
    channel.exchange_declare(exchange=exchange_name, exchange_type='direct', durable=True)
    route_key = queue_name = 'Convert_User_'+ application_environment
    channel.queue_bind(exchange=exchange_name, queue=queue_name, routing_key=route_key)
except Exception as channel_declaration:
    print(channel_declaration)   
    response = {'channel_exception': 'Unable to connect to RMQ', 'msg': channel_declaration}
    logger('accounts_worker_log',response) 

#Receiving messages from the queue is more complex. It works by subscribing a callback function to a queue.
#Whenever we receive a message, this callback function is called by the Pika library.

def update_user_fanout(q_data):
    try:
        q_data = json.dumps(q_data)
        connection_pdf = pika.BlockingConnection(parameters)
        channel_pdf = connection_pdf.channel()
        channel_pdf.exchange_declare(exchange='Update_User_Xchange', exchange_type='fanout', durable=True)
        channel_pdf.basic_publish(exchange='Update_User_Xchange', routing_key='', body=q_data)
        connection_pdf.close()
        return  {'status':True}
    except Exception as userException:
        return  {'status':False, 'msg': userException}
        
def update_user_profile_fanout(q_data):
    try:
        q_data = json.dumps(q_data)
        connection_pdf = pika.BlockingConnection(parameters)
        channel_pdf = connection_pdf.channel()
        channel_pdf.exchange_declare(exchange='Update_User_Profile_Xchange', exchange_type='fanout', durable=True)
        channel_pdf.basic_publish(exchange='Update_User_Profile_Xchange', routing_key='', body=q_data)
        connection_pdf.close()
        return  {'status':True}
    except Exception as userException:
        return  {'status':False, 'msg': userException}    

def updateUser(content, log_id):
    global userMongoData, refkey
    ''' This content will have userid and pin that needs to be updated to the userser in Mongo, Mysql and Redis'''
    userData = {}
    try:    
        if content.get('digilockerid') != None:        
            try:
                if content.get('digilockerid') != None:    
                    userData['digilockerid'] = content['digilockerid']
                user_type = content.get('user_type')
                if content.get('mobile_no') != None:    
                    userData['mobile_no'] = content['mobile_no']
                if content.get('device_id') != None:    
                    userData['device_id'] = content['device_id']
                if content.get('email_id') != None:    
                    userData['email_id'] = content.get('email_id')
                if user_type != None:    
                    userData['user_type'] = user_type
                if content.get('uid') != None:    
                    userData['uid'] = content.get('uid')
                if content.get('uid_token') != None:    
                    userData['uid_token'] = content.get('uid_token')  
                    userData['uid_token_hash'] = hashlib.md5(content.get('uid_token').encode('utf-8')).hexdigest()                 
                qdata = {"data": userData, "id": log_id}    
                return update_user_fanout(qdata)                         
            except Exception as dataError: 
                return {'status':False, 'msg':dataError}   
        else:            
            return {'status': False, 'msg' : 'digilockerid not defined'}
                 
    except Exception as dataError:
        return {'status':False, 'msg':dataError}   

def updateUserProfile(content, log_id):
    global userMongoData, refkey
    ''' This content will have userid and pin that needs to be updated to the userser in Mongo, Mysql and Redis'''
    userProfileMongoData = {}
    try:    
        if content.get('digilockerid') != None:        
            try: 
                if content.get('digilockerid') != None:    
                    userProfileMongoData['digilockerid'] = content['digilockerid']
                user_type = content.get('user_type')
                now = datetime.datetime.now()
                if content.get('date_of_birth') != None:
                    userProfileMongoData['date_of_birth'] = content['date_of_birth']   
                if content.get('name') != None:    
                    userProfileMongoData['name'] = content['name']
                if content.get('gender') != None:    
                    userProfileMongoData['gender'] = content['gender']
                if content.get('state') != None:    
                    userProfileMongoData['state'] = content.get('state')
                if content.get('district') != None:    
                    userProfileMongoData['district'] = content.get('district')
                if content.get('city') != None:
                    userProfileMongoData['city'] = content.get('city')     
                               
                userProfileMongoData['converted_on'] = datetime.datetime.today().strftime('%Y-%m-%d')
                userProfileMongoData['converted_by'] = user_type                  
                qdata = {"data": userProfileMongoData, "id": log_id}                    
                return update_user_profile_fanout(qdata)                         
            except Exception as dataError: 
                return {'status':False, 'msg':dataError}   
        else:            
            return {'status': False, 'msg' : 'digilockerid not defined'}                 
    except Exception as dataError:
        return {'status':False, 'msg':dataError}          

#Receiving messages from the queue is more complex. It works by subscribing a callback function to a queue.
#Whenever we receive a message, this callback function is called by the Pika library.

def updateUserAndProfile(ch, method, properties, content, log_id):
    try:
        update_user = updateUser(content, log_id)
        update_user_profile = updateUserProfile(content, log_id)
        ch.basic_ack(delivery_tag=method.delivery_tag,multiple=False)
        response =  {'convert_user_logs': {'update_user_resp': update_user,'update_user_profile_resp': update_user_profile }}
        logger('accounts_worker_log',response) 
    except Exception as e:
        ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
        return {'status':False, 'msg':e}  

def callback(ch, method, properties, body):      
    try:      
        data = json.loads(body)
        content = data['data']     
        user_type = content.get('user_type')    
        response =  {'convert_user_logs': {'accounts_type': user_type}}
        logger('accounts_worker_log',response) 

        try:
            log_id = data.get('id')
            if(content['digilockerid'] == None or content['digilockerid'] == False):
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)                     
            elif user_type == 'aadhaar':
                validation_resp = validation(content, 'aadhaar')                
                if validation_resp['status'] == True:
                    updateUserAndProfile(ch, method, properties, content, log_id)
                else:
                    response =  {'convert_user_exception': 'validation of data failed', 'data': content}
                    logger('accounts_worker_log',response)    
            elif user_type == 'demographic':   
                validation_resp = validation(content, 'demographic')       
                if validation_resp['status'] == True:
                    updateUserAndProfile(ch, method, properties, content, log_id)
                else:
                    response =  {'convert_user_exception': 'validation of data failed', 'data': content}
                    logger('accounts_worker_log',response)  
            else:
                response =  {'convert_user_exception': 'user_type not_defined'}
                logger('accounts_worker_log',response) 
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False) 
                      
        except Exception as flowExpn: 
            ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)
            response = {'convert_user_exception': 'Data Exception.', 'data_exception': flowExpn}
            logger('accounts_worker_log',response)
            print('flowExpn')
    
    except Exception as data_expn: 
        response = {'convert_user_exception': 'Data Exception.', 'data_exception': data_expn}
        logger('accounts_worker_log',response)         
        ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)  
        
try:
    channel.basic_qos(prefetch_count= int(prefetch_counter['split-convert-user'])) 
    channel.basic_consume(queue=queue_name, on_message_callback = callback)
    channel.start_consuming()   
except Exception as ChannelException:
    response =  {'channelException': ChannelException}
    logger('accounts_worker_log',response)     
    print(ChannelException)    
    print('Unable to communicate with RMQ')
    channel.stop_consuming()