import zipfile

from modules.logs.log_config import get_logger
from patch.include import *
from tqdm import tqdm


@app_context(commit=True)
def patch():
    """
    Для тестирования патча: ( cd /opt/crm; python3 -m patch.20220XXXXXXX_PATCHNAME )
    Здесь можно работать с моделями через models.CmfTask и т.д.
    Для прогрессбара используйте:
    for task in tqdm(models.CmfTask.list()):
        ...
    """
    logger = get_logger('patch')
    root = '/opt/var/backup/files'
    os_max_length = 127
    for code in tqdm(os.listdir(root)):
        document = models.CmfDocument.get(code=code)
        logger.info(f'Документ {document}')
        for filename in os.listdir(f'{root}/{code}'):
            file_path = f'{root}/{code}/{filename}'
            if zipfile.is_zipfile(file_path):
                with zipfile.ZipFile(file_path) as myzip:
                    for zip_attach_name in myzip.namelist():
                        if zip_attach_name.endswith('/'):
                            continue
                        logger.info(f'zip_attach_name={zip_attach_name}')
                        attach_name = zip_attach_name
                        ext = ''
                        if len(attach_name.split('.')) > 0:
                            ext = attach_name.split('.')[-1]
                        if len(attach_name) > os_max_length:
                            attach_name = f'{attach_name[:os_max_length - (5 + len(ext))].strip()}-{short_str_enc(attach_name, 4)}'
                            if ext:
                                attach_name = f'{attach_name}.{ext}'
                            logger.info(f'Новое имя: {attach_name}')
                        attach = models.CmfAttachment.get(filter=[['parent', '==', document],
                                                                  ['name', '==', attach_name]], fields=['url'])
                        if not attach:
                            attach = models.CmfAttachment(parent=document, name=attach_name)
                            attach.save()
                            logger.info(f'Создали вложение {attach}')
                        try:
                            with myzip.open(zip_attach_name) as attach_f:
                                attach.upload_file(attach_f.read(), overwrite=True)
                            logger.info(f'Загрузили {attach.url}')
                        except Exception as e:
                            logger.error(f'Не удалось сохранить {zip_attach_name}: {e}')
            else:
                logger.info(f'attach_name={filename}')
                attach = models.CmfAttachment.get(filter=[['parent', '==', document],
                                                          ['name', '==', filename]], fields=['url'])
                if not attach:
                    attach = models.CmfAttachment(parent=document, name=filename)
                    attach.save()
                    logger.info(f'Создали вложение {attach}')
                with myzip.open(file_path) as attach_f:
                    attach.upload_file(attach_f.read(), overwrite=True)
                logger.info(f'Загрузили {attach.url}')

        models.CmfDocument.dp.commit()


import hashlib

enc_table_16 = "0123456789abcdef"
enc_table_64 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"


def int_to_enc(n, enc_table):
    """Encode integer into string, using digit encoding table.

    You can encode integer into hexadecimal string:

    >>> int_to_enc(20190925, enc_table_16)
    '13416cd'

    To verify use, python's hex() function:

    >>> hex(20190925)
    '0x13416cd'

    You can encode integer using 64 digit table:

    >>> int_to_enc(20190925, enc_table_64)
    '1d1rd'

    """
    if n == 0:
        return enc_table[0]

    base = len(enc_table)
    digits = ""
    while n:
        digits += enc_table[int(n % base)]
        n //= base
    return digits[::-1]

def short_str_enc(s, char_length=8, enc_table=enc_table_64):
    """Geneate string hash with given length, using specified encoding table.

    Example 1:

    Generating hash with length of 8 characters, using hexadecimal encoding table:

    >>> short_str_enc("hello world", 8, enc_table_16)
    '309ecc48'

    Generating hash with length of 8 charactes, using extended 64-digit encoding table:

    >>> short_str_enc("hello world", 8, enc_table_64)
    'MDIN8D1b'
    """

    if char_length > 128:
        raise ValueError("char_length {} exceeds 128".format(char_length))
    hash_object = hashlib.sha512(s.encode())
    hash_hex = hash_object.hexdigest()
    hash_enc = int_to_enc(int(hash_hex, 16), enc_table)
    return hash_enc[0:char_length]


if __name__ == "__main__":
    patch()
