"""
Port de SuiteClientes.ReglasNegocio.General.Encrypt/Decrypt (C#) a Python.

Algoritmo: AES-256-CBC con clave derivada por PBKDF2-SHA1 (Rfc2898DeriveBytes,
1000 iteraciones por defecto). El IV es ASCII fijo de 16 bytes.

Padding: el codigo C# usa PaddingMode.Zeros al cifrar y PaddingMode.None al
descifrar (recortando \\0 al final). Replicamos exactamente eso para que el
texto cifrado sea bit-a-bit identico al que produce el cliente MIG.
"""
from __future__ import annotations

import base64
import os
from typing import Optional

from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA1


PASSWORD = os.getenv('AES_PASSWORD', 'luc1an@1210')
SALT     = os.getenv('AES_SALT',     'r1b1s0f7').encode('ascii')
IV       = os.getenv('AES_IV',       '@1B2c3D4e5F6g7H8').encode('ascii')

_KEY_CACHE: Optional[bytes] = None


def _key() -> bytes:
    global _KEY_CACHE
    if _KEY_CACHE is None:
        _KEY_CACHE = PBKDF2(PASSWORD, SALT, dkLen=32, count=1000, hmac_hash_module=SHA1)
    return _KEY_CACHE


def _pad_zeros(data: bytes, block: int = 16) -> bytes:
    rem = len(data) % block
    if rem == 0:
        return data
    return data + b'\x00' * (block - rem)


def encrypt(plain_text: str) -> str:
    if plain_text is None:
        plain_text = ''
    cipher = AES.new(_key(), AES.MODE_CBC, IV)
    data = _pad_zeros(plain_text.encode('utf-8'))
    encrypted = cipher.encrypt(data)
    return base64.b64encode(encrypted).decode('ascii')


def decrypt(encrypted_text: str) -> str:
    if not encrypted_text:
        return ''
    cipher = AES.new(_key(), AES.MODE_CBC, IV)
    data = base64.b64decode(encrypted_text)
    decrypted = cipher.decrypt(data)
    return decrypted.decode('utf-8', errors='ignore').rstrip('\x00')


if __name__ == '__main__':
    plain = '900123456712'
    enc = encrypt(plain)
    print(f'plain   : {plain}')
    print(f'encrypt : {enc}')
    print(f'decrypt : {decrypt(enc)}')
    assert decrypt(enc) == plain, 'roundtrip falla'
    print('OK')
