'''
Created on Jun 12, 2020

@author Saurabh Kumar Pandey
'''
import pika
import json
import datetime
from datetime import date, timedelta

import hashlib 
from pymongo import MongoClient,errors
from configparser import ConfigParser

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"]
accounts_db = config_object["mongo_accounts_db"]


def create_user(ch, method, properties, content, user_type, log_id):      
    try:         
        userName = content['digilockerid']
        mobileno = content.get('mobile_no') or ''
        aadhaarno = content.get('uid') or ''
        user_type = content['user_type'] or 'non_aadhaar'
        
        now = datetime.datetime.now()
        formatted_date = now.strftime('%Y-%m-%d %H:%M:%S')          
          
        ''' 
         try here to commit to mongodb 
         once commited check the response true and then attempt for Redis     
        '''
        mongoresp = insertMongoUser(content)
        if mongoresp['status'] == True and mongoresp['msg'][0] == 1:   
            try:                  
                mongorespProfile = insertMongoUserProfile(content)                
                if mongorespProfile['status'] == True and mongorespProfile['msg'][0] == 1: 
                    ch.basic_ack(delivery_tag=method.delivery_tag,multiple=False)
                    return {'status': True, 'msg' : 'UserCreated'}               
                  
                elif mongorespProfile['status'] == False:
                    if mongorespProfile['code']  == 2 : 
                        ch.basic_ack(delivery_tag=method.delivery_tag,multiple=False)
                        return {'status': True, 'msg' : 'UserCreated'}   
                    elif mongorespProfile['code'] == 3:
                        #delete from users
                        ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)
                        return {'status': False, 'msg' : ('profileWrite', 'error 214')}
                else:
                    ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True) 
                    return {'status': False, 'msg' : ('profileWrite', 'error 217')}
                    
            except Exception as profileExn:
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
                return {'status': False, 'msg' : [('profileWrite', 'error'), ('profileExn', profileExn)] }
                
        elif mongoresp['status'] == False and mongoresp['code'] == 2 :
            try:  
                mongorespProfile = insertMongoUserProfile(content)                
                if mongorespProfile['status'] == True and mongorespProfile['msg'][0] == 1:                        
                    ch.basic_ack(delivery_tag=method.delivery_tag,multiple=False)
                    return {'status': True, 'msg' : 'UserCreated'}               
                  
                elif mongorespProfile['status'] == False:
                    if mongorespProfile['code']  == 2 : 
                        ch.basic_ack(delivery_tag=method.delivery_tag,multiple=False)
                        return {'status': True, 'msg' : 'UserCreated'}   
                    elif mongorespProfile['code'] == 3:
                        #delete from users
                        ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)
                        return {'status': False, 'msg' : ('profileWrite', 'error')}
                else:
                    ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True) 
                    return {'status': False, 'msg' : ('profileWrite', 'error')}
                    
            except Exception as profileExn:
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True) 
                return {'status': False, 'msg' : ('profileExn', profileExn)}
                    
        elif mongoresp['status'] == False and mongoresp['code'] == 3 :
            ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False) 
            return {'status': False, 'msg' : ('MongoDb Failed to write', mongoresp)}
                     
        else:
            #condition for mongodb Failures insert 
            ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True) 
            return {'status': False, 'msg' : ('MongoDb Failed to write', mongoresp)} 
    except Exception as funcExcpn: 
        ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False) 
        return {'status': False, 'msg' : ('Functional Error--', funcExcpn)}

def insertMongoUser(content): 
    try: 
        global userMongoData, dataError, rmqPublishError
        userType = content['user_type']
        
        uid_tokenHash = ''
        aadhaarNumberHash = '' 
        userMongoData = {}
        userMongoData = {}
        insprofileData = {}
        userProfileMongoData = {}
        now = datetime.datetime.now()  
        
        try:
            dob = datetime.datetime.strptime(content['date_of_birth'], '%Y-%m-%d')  
            userMongoData['digilockerid'] = content['digilockerid']
            userMongoData['created_by'] = content['digilockerid']
            userMongoData['mobile_no'] = content.get('mobile_no')            
            userMongoData['user_id'] = content.get('user_id')   
            userMongoData['user_alias'] = content.get('user_alias')   
            userMongoData['password'] = content.get('password')
            userMongoData['pin'] = content.get('pin')
            userMongoData['user_type'] = userType
            userMongoData['user_enc_key'] = content.get('user_enc_key') 
            userMongoData['device_id'] = content.get('device_id')     
            userMongoData['is_deleted'] = False
            userMongoData['created_on'] = now
            userMongoData['email_id'] = content.get('email_id')  
            userMongoData['security_question_id'] = content.get('security_question_id')
            userMongoData['email_id_verified'] = 0      
            userMongoData['data_directory_type'] = content.get('data_directory_type') or 'v3'
            userMongoData['drive_migration'] = content.get('drive_migration') or 'v6'  
            
            userMongoData['option1'] = content.get('option1') or 'N'
            userMongoData['option2'] = content.get('option2') or 'N'
            userMongoData['option3'] = content.get('option3') or 'N'
            
            if  userType == "aadhaar" or userType == "demographic":                     
                aadhaarNo = content.get('uid')                        
                uidToken = content.get('uid_token')
                userMongoData['uid_token'] = uidToken
                userMongoData['uid_token_hash'] = hashlib.md5(uidToken.encode('utf-8')).hexdigest() 
                userMongoData['uid'] = aadhaarNo
                
            try:
                data = {  
                "operation": "C",
                "db": accounts_db["accounts_db"],
                "collection":'users',
                "data": userMongoData
                } 
                
                insData = mongoInsertOperator(data)
                insData['dataResp'] = userMongoData                
                return insData 
            except Exception as rmqPublishError:
                return {'status':False, 'msg':rmqPublishError}
                     
        except Exception as dataError:
            return {'status':False, 'msg':dataError}
    
    except Exception as functionalError:
        return {'status':False, 'msg':functionalError}    
    
def insertMongoUserProfile(content): 
    try: 
        global userMongoData, dataError, rmqPublishError
        userType = content['user_type']
        
        uid_tokenHash = ''
        aadhaarNumberHash = '' 
        userProfileMongoData = {}
        insprofileData = {}
        userProfileMongoData = {}
        now = datetime.datetime.now()  
        userType = content['user_type']
        
        try:
            dob = datetime.datetime.strptime(content['date_of_birth'], '%Y-%m-%d')               
            userProfileMongoData['digilockerid'] = content['digilockerid']
            userProfileMongoData['name'] = content['name']
            userProfileMongoData['date_of_birth'] = dob
            userProfileMongoData['gender'] = content['gender']
            userProfileMongoData['email_id'] = content.get('email_id')
            userProfileMongoData['state'] = content.get('state')
            userProfileMongoData['district'] = content.get('district')
            userProfileMongoData['city'] = content.get('city')
            userProfileMongoData['mobile_no'] = content.get('mobile_no')
            userProfileMongoData['photo'] = ''
            userProfileMongoData['user_from'] = content['user_from']
            userProfileMongoData['account_type'] = userType
            userProfileMongoData['uploaded_doc_path'] = None
            userProfileMongoData['health_doc_path'] = None
            userProfileMongoData['issued_doc_path'] = None
            userProfileMongoData['created_on'] = now    
            userProfileMongoData['email_subscription'] = 1     
                
            try:
                profile_data = {  
                "operation": "C",
                "db":accounts_db["accounts_db"],
                "collection":'users_profile',
                "data": userProfileMongoData
                } 
                insData = mongoInsertOperator(profile_data)     
                insData['dataResp'] = userMongoData           
                return insData 
            except Exception as rmqPublishError:
                return {'status':False, 'msg':rmqPublishError}
                     
        except Exception as dataError:
            return {'status':False, 'msg':dataError}
    
    except Exception as functionalError:
        return {'status':False, 'msg':functionalError}        
     
def mongoInsertOperator(data):
    response_code = 0
    try:    
        crud_operation = str(data["operation"])  
        data_value = data["data"]
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        uri_value = accounts_db[db_name]
        if uri_value != None:           
            try:
                response_code = 1
                    #Insert Operation in MongoDB.
                if crud_operation == 'C':
                    mongo_client = MongoClient(accounts_db[db_name])
                    mongo_client[db_name][mongo_collection_name].insert_one(data_value)
                    return {'status':True, 'msg':(response_code, 'Success'), 'code':response_code}
            except errors.DuplicateKeyError as duplicaterror: 
                response_code = 2
                return {'status':False, 'msg': duplicaterror, 'code':response_code }  
            except Exception as Crudexception:
                response_code = 3
                if 'document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping' in Crudexception:
                    errr = Crudexception
                    return {'status':False, 'msg':errr, 'code':response_code}
                else:
                    response_code = 5
                    return {'status':False, 'msg':Crudexception, 'code':response_code, 'uri': uri_value}
        else:
            response_code = 4
            return {'status':False, 'msg':"There is no database available for the client request.", 'code':response_code}
    except Exception as JSONError:
        err = JSONError
        return {'status':False, 'msg':err, 'code':response_code}
    
def mongoUpdateOperator(data):
    global response_code   
    #Decoding the json data received from the client.
    response_code = 'OK'
    code = 0    
    try:    
#         data = json.loads(body)
        crud_operation = str(data["operation"])  
        data_value = data["data"]
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        uri_value = accounts_db[db_name]
        if uri_value != None:       
            try:
                #Update Operation in MongoDB.
                if crud_operation == 'U':
                    mongo_client = MongoClient(accounts_db[db_name])
                    update_key_value =  data["key"]
                    if update_key_value == "" or update_key_value == None:
                        response_code = 'NKEY'
                        #If no key is received from the client,then the request will be rejected.
                        response_code = ("There is no key value available for the UPDATE operation.")
                        return {'status':False, 'msg':response_code, 'code': code} 
                    else:
                        mongo_client[db_name][mongo_collection_name].update_one(update_key_value, {"$set":data_value})
                        code = 1    
                        return {'status':True, 'msg':(code, response_code), 'code': code} 
            except errors.DuplicateKeyError as duplicaterror:
                code = 2
                response_code = duplicaterror
                return {'status':False, 'msg':(code, response_code), 'code': code}
            except Exception as Crudexception:
                code = 3
                response_code = Crudexception
                if 'document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping' in Crudexception:
                    response_code = Crudexception
                elif 'filter must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping' in Crudexception:
                    response_code = Crudexception   
                else:
                    response_code = Crudexception               
                
                return {'status':False, 'msg':(code, response_code), 'code': code}    
        else:
            code = 4
            response_code = ("There is no database available for the client request.")  
            return {'status':False, 'msg':(code, response_code), 'code': code}              
    except Exception as JSONError:
        code = 5
        response_code = JSONError
        return {'status':False, 'msg':(code, response_code), 'code': code} 
  
def mongoUpsertOperator(data):
    global response_code   
    #Decoding the json data received from the client.
    response_code = 'OK'
    code = 0    
    try:    
#         data = json.loads(body)
        crud_operation = str(data["operation"])  
        data_value = data["data"]
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        uri_value = accounts_db[db_name]
        if uri_value != None:       
            try:
                #Update Operation in MongoDB.
                if crud_operation == 'U':
                    mongo_client = MongoClient(accounts_db[db_name])
                    update_key_value =  data["key"]
                    update_many = data["multi"]
                    if update_key_value == "" or update_key_value == None:
                        response_code = 'NKEY'
                        #If no key is received from the client,then the request will be rejected.
                        response_code = ("There is no key value available for the UPDATE operation.")
                    else:
                        if update_many == "N":
                            response_code = 1
                            ret = mongo_client[db_name][mongo_collection_name].update_one(update_key_value, {"$set":data_value}, upsert=True)
                            return {'status':True, 'msg':ret, 'code': response_code}     
                        else:
                            response_code = 0
                            response_code = ('else error')
                            return {'status':False, 'msg':response_code, 'code': response_code} 
            except errors.DuplicateKeyError as duplicaterror:
                code = 2
                response_code = duplicaterror
                return {'status':False, 'msg':response_code, 'code': code}   
            except Exception as Crudexception:
                code = 3
                response_code = Crudexception
                if 'document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping' in Crudexception:
                    response_code = Crudexception
                elif 'filter must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping' in Crudexception:
                    response_code = Crudexception   
                else:
                    response_code = Crudexception   
                return {'status':False, 'msg':Crudexception, 'code': code}                 
        else:
            code = 4
            response_code = ("There is no database available for the client request.")        
            return {'status':False, 'msg':JSONError, 'code': code}          
    except Exception as JSONError:
        code = 5
        return {'status':False, 'msg':JSONError, 'code': code}  
        
    return {'status':True, 'msg':response_code, 'code': code}     
        
def mongoUpdateLog(data):
    global response_code   
    #Decoding the json data received from the client.
    response_code = 'OK'
    code = 0    
    try:    
#         data = json.loads(body)
        crud_operation = str(data["operation"])  
        data_value = data["data"]
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        uri_value = accounts_db[db_name]
        if uri_value != None:       
            try:
                #Update Operation in MongoDB.
                if crud_operation == 'U':
                    mongo_client = MongoClient(accounts_db[db_name])
                    update_key_value =  data["key"]
                    if update_key_value == "" or update_key_value == None:
                        response_code = 'NKEY'
                        #If no key is received from the client,then the request will be rejected.
                        response_code = ("There is no key value available for the UPDATE operation.")
                        return {'status':False, 'msg':response_code, 'code': code} 
                    else:
                        print(mongo_client[db_name][mongo_collection_name].update_one(update_key_value, {"$push":data_value}))
                        code = 1    
                        return {'status':True, 'msg':(code, response_code), 'code': code} 
            except errors.DuplicateKeyError as duplicaterror:
                code = 2
                response_code = duplicaterror
                return {'status':False, 'msg':(code, response_code), 'code': code}
            except Exception as Crudexception:
                code = 3
                response_code = Crudexception
                if 'document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping' in Crudexception:
                    response_code = Crudexception
                elif 'filter must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping' in Crudexception:
                    response_code = Crudexception   
                else:
                    response_code = Crudexception               
                
                return {'status':False, 'msg':(code, response_code), 'code': code}    
        else:
            code = 4
            response_code = ("There is no database available for the client request.")  
            return {'status':False, 'msg':(code, response_code), 'code': code}              
    except Exception as JSONError:
        code = 5
        response_code = JSONError
        return {'status':False, 'msg':(code, response_code), 'code': code}   

def selectMongoData(data):
    try:
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        mongo_client = MongoClient(accounts_db[db_name])
        query = data.get('query')
        projection = data.get('projection')    
        dataArray = []   
        for data in mongo_client[db_name][mongo_collection_name].find(query, projection):
            dataArray.append(data)        
        return [{
            "total" : len(dataArray),
            "items" : dataArray
            }]             
    except Exception as genExcn:    
        return genExcn

def mongoPushSliceLog(data):
    global response_code   
    #Decoding the json data received from the client.
    response_code = 'OK'
    code = 0    
    try:    
#         data = json.loads(body)
        crud_operation = str(data["operation"])  
        data_value = data["data"]
        db_name = str(data["db"])
        mongo_collection_name = str(data["collection"])
        uri_value = accounts_db[db_name]
        if uri_value != None:       
            try:
                #Update Operation in MongoDB.
                if crud_operation == 'U':
                    mongo_client = MongoClient(accounts_db[db_name])
                    update_key_value =  data["key"]
                    if update_key_value == "" or update_key_value == None:
                        response_code = 'NKEY'
                        #If no key is received from the client,then the request will be rejected.
                        response_code = ("There is no key value available for the UPDATE operation.")
                        return {'status':False, 'msg':response_code, 'code': code} 
                    else:
                        print(mongo_client[db_name][mongo_collection_name].update_one(update_key_value, {"$push":data_value}))
                        code = 1    
                        return {'status':True, 'msg':(code, response_code), 'code': code} 
            except errors.DuplicateKeyError as duplicaterror:
                code = 2
                response_code = duplicaterror
                return {'status':False, 'msg':(code, response_code), 'code': code}
            except Exception as Crudexception:
                code = 3
                response_code = Crudexception
                if 'document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping' in Crudexception:
                    response_code = Crudexception
                elif 'filter must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping' in Crudexception:
                    response_code = Crudexception   
                else:
                    response_code = Crudexception               
                
                return {'status':False, 'msg':(code, response_code), 'code': code}    
        else:
            code = 4
            response_code = ("There is no database available for the client request.")  
            return {'status':False, 'msg':(code, response_code), 'code': code}              
    except Exception as JSONError:
        code = 5
        response_code = JSONError
        return {'status':False, 'msg':(code, response_code), 'code': code}     
      
#entry point OVER    