from Crypto.Cipher import Blowfish

def Blowfish_encrypt(input:str,key:str,mode,iv:str) -> str:
    try:
        checkHex(input,'Input')
        input = bytes.fromhex(input)
        key = bytes.fromhex(key)
        iv = bytes.fromhex(iv)
        input = pad(input,Blowfish.block_size)
        if mode=='ECB':
            modeName = getattr(Blowfish,f"MODE_{mode}")
            cipher = Blowfish.new(key, modeName)
        else:
            modeName = getattr(Blowfish,f"MODE_{mode}")
            cipher = Blowfish.new(key, modeName, iv=iv)
        return cipher.encrypt(input).hex()
    except ValueError as e:
        return str(e)

def Blowfish_decrypt(input:str,key:str,mode,iv:str) -> str:
    try:
        checkHex(input,'Input')
        input = bytes.fromhex(input)
        key = bytes.fromhex(key)
        iv = bytes.fromhex(iv)
        if mode == 'ECB':
            modeName = getattr(Blowfish,f"MODE_{mode}")
            cipher = Blowfish.new(key, modeName)
        else:
            modeName = getattr(Blowfish,f"MODE_{mode}")
            cipher = Blowfish.new(key, modeName, iv=iv)
        return unpad(cipher.decrypt(input),Blowfish.block_size).hex()#.decode()
    except ValueError as e:
        return str(e)
    except AttributeError as e:
        return "Decrypting Invalid mode or Other Invalid Key or Input"
def pad(data:bytes, block_size:int) -> bytes:
    padlength=block_size - (len(data)%block_size)
    return data+bytes([padlength]*padlength)

def unpad(data:bytes, block_size:int) -> bytes:
    try:
        if len(data)%block_size!=0:
            raise ValueError('Not padded')
        if not _is_valid_Padding(data):
            raise ValueError('Invalid padding')
        return data[:-data[-1]]#data[-1] is the last byte. Which is the number of bytes padded. So :-data[-1] cuts off the last data[-1] bytes.
    except ValueError as e:
        return str(e)

def _is_valid_Padding(data:bytes) -> bool:
    pad = data[-1]
    return data[-pad:] == bytes([pad]) * pad



def checkHex(text,cheking):
    if len(text)%2 != 0:
        raise ValueError(f"{cheking}: Invalid hex length")
    hexdigits = '0123456789abcdefABCDEF'
    for i in text:
        if i not in hexdigits:
            raise ValueError(f"{cheking}: Invalid hex")
    return True
