import os
import pytz
import base64
import requests
from datetime import datetime
from hashlib import sha1
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup
import string
import json
import sys
#import M2Crypto
#from M2crypto import BIO, RSA
import Crypto
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
import random
import xmltodict



class proxy():
    """Class to handle building of the soap client
    """
    
    def __init__(self, wsdl, username, password,certificate_file_path):
        """Constructor

        Arguments:
            wsdl {str} -- WSDL url
            username {str} -- Username
            password {str} -- Password
        """
        self.wsdl = wsdl
        self.username = username
        self.password = password 
        
        #self.encrypt_pwd_with_PyCrypto(password, certificate_file_path)

    def generatenonce_asbytes(self):
        """Generates Nonce as bytes
        """
        return os.urandom(16)

    def generatenonce_asbytearray(self):
        """Generates Nonce as bytearray
        """
        return bytearray(os.urandom(16))

    def create_requesttimestamp(self):
        """Creates timestamp to be used when
        sending request
        """
        utc_now = pytz.utc.localize(datetime.utcnow())
        eat_now = utc_now.astimezone(pytz.timezone('Africa/Kampala'))
        eat_time = eat_now.isoformat()

        timestamp = '{}+03:00'.format(eat_time[:-9])

        return timestamp

    def create_timestamp(self):
        """Create timestamp
        """
        utc_now = pytz.utc.localize(datetime.utcnow())
        eat_now = utc_now.astimezone(pytz.timezone('Africa/Kampala'))
        eat_time = eat_now.isoformat()

        return eat_time

    def timestamp_forrequest(self, timestamp):
        """Formats timestamp for request

        Arguments:
            timestamp {string} -- Timestamp in the format
            to be sent with the request as Created
        """
        return '{}+03:00'.format(timestamp[:-9])

    def timestamp_fordigest(self, timestamp):
        """Formates timestamp for digest

        Arguments:
            timestamp {string} -- Timestamp in the format
            to be used to creat password digest
        """
        return '{}+0300'.format(timestamp[:-9])

    def gettimestamp_asbytes(self, timestamp):
        """Gets timestamp as bytes

        Arguments:
            timestamp {str} -- Timestamp
        """
        return timestamp.encode('utf-8')

    def hashpassword_withdigest(self):
        """Hash password using sha1.digest() function
        """
        return sha1(self.password.encode('utf-8')).digest()

    def generatedigest_withbytesvalues(self, nonce, created, password_hash):
        """Generates password digest using sha1.digest
        function

        Arguments:
            nonce {bytes} -- Nonce
            created {bytes} -- Created
            password_hash {bytes} -- Hashed password
        """
        combined_bytearray = bytearray()

        combined_bytearray.extend(nonce)
        combined_bytearray.extend(created)
        combined_bytearray.extend(password_hash)

        encoded_digest = sha1(combined_bytearray).digest()

        password_digest = base64.b64encode(encoded_digest)

        return password_digest.decode('utf-8')


        

    def get_person(self,nationalId):
        """
         Build request to get person
        """
        username = self.username
        nonce_bytes = self.generatenonce_asbytes()
        nonce = base64.b64encode(nonce_bytes).decode('utf-8')
        timestamp = self.create_timestamp()
        created_digest = self.timestamp_fordigest(timestamp)
        created_digest_bytes = self.gettimestamp_asbytes(created_digest)

        passwordhash_bytes = self.hashpassword_withdigest()

        password_digest = self.generatedigest_withbytesvalues(nonce_bytes, created_digest_bytes, passwordhash_bytes)

        created_request = self.timestamp_forrequest(timestamp)

        body = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fac="http://facade.server.pilatus.thirdparty.tidis.muehlbauer.de/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><soapenv:Header><wsse:UsernameToken><wsse:Username>{0}</wsse:Username><wsse:Password Type="PasswordDigest">{1}</wsse:Password><wsse:Nonce>{2}</wsse:Nonce><wsse:Created>{3}</wsse:Created></wsse:UsernameToken></soapenv:Header><soapenv:Body><fac:getPerson><!--Optional:--><request><!--Optional:--><nationalId>{4}</nationalId></request></fac:getPerson></soapenv:Body></soapenv:Envelope>'.format(username,password_digest,nonce,created_request,nationalId)

        return body

    def send_request(self, body):
        """Sends the SOAP request
        """        
        url = self.wsdl
        headers = {'Content-Type':'text/xml'}
        response = requests.post(url, data=body, headers=headers)
        
        return response.content.decode('utf-8')
     

    def parse_request(self, response):
        """Parses the response returned from the API request
        
        Arguments:
            response {string} -- API response

        Returns:
            tuple (transactionStatus, cardStatus, matchingStatus)
        """
        soup = BeautifulSoup(response, 'lxml-xml')

        if soup.find_all('transactionStatus')[1].string == 'Ok':
            retn = []
            rt = soup.find('return')
            retn = str(rt)
            
            return json.dumps(xmltodict.parse(retn))

        else:
            retn2 = []
            rt = soup.find_all('return')
            
            retn2 = str(rt)
            return json.dumps(xmltodict.parse(retn2))


    def password_generator(self):
        """Generates password.
        """
        #special_characters = '!@%/()=?+.-_'
        special_characters = '@!#_+$%*'
        password_list = (
            [
                random.choice(special_characters),
                random.choice(string.digits),
                random.choice(string.ascii_lowercase),
                random.choice(string.ascii_uppercase)
            ]
            +
            [
                random.choice(
                    string.digits +
                    string.ascii_lowercase +
                    string.ascii_uppercase +
                    special_characters +
                    string.digits
                )
                for i in range(5)
            ]
        )

        random.shuffle(password_list)
        password = ''.join(password_list)
        return password
    

    def encrypt_pwd_with_PyCrypto(self,raw_password, certificate_file_path):
        """
        Encrypts raw pasword.

        """
        message = raw_password.encode('utf-8')
         
        the_pubkey = RSA.importKey(open(certificate_file_path, 'r').read())
              
        cipher = PKCS1_v1_5.new(the_pubkey)

        ciphertext = cipher.encrypt(message)

        pwd_to_base_64 = base64.b64encode(ciphertext).decode('utf-8')
        #pwd_to_base_64 = base64.b64encode(ciphertext)
        
        return pwd_to_base_64
