# -*- coding: utf-8 -*-

import datetime
import os
import secrets
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad


KEY_PATTERN = bytes([0xFE, 0xEB, 0xDA, 0xED, 0x37, 0x33, 0x13])
CONFIG_PATTERN = bytes([0x31, 0x33, 0x73, 0xDE, 0xAD, 0xBE, 0xEF])
BUILD_DATE = datetime.datetime.now().strftime('%Y-%m-%d')


def file_get_data(filepath):
    data = bytearray()
    try:
        with open(filepath, 'rb') as file:
            data = bytearray(file.read())
    except FileNotFoundError:
        print(f"Error: File not found at {filepath}")
        return data
    
    except Exception as e:
        print(f"Error reading file: {e}")
        return data

    if not data:
        print(f"Error: file {filepath} is empty")
        return data
    
    return data


def file_save(filepath, data):
    file_dir = os.path.dirname(filepath)
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)

    try:
        with open(filepath, 'wb') as file:
            file.write(data)
    except Exception as e:
        print(f"Error writing to file: {e}")
        return False
    return True


def find_pattern(data, pattern:bytes) -> int:
    index = data.find(pattern)
    if index == -1:
        print(f"Pattern {pattern} not found in the file.")
        return -1
    else:
        return index
    

def get_random_key(size=16):
    return secrets.token_bytes(size)


def encrypt_data(data, key):
    # Получаем размер данных
    size = len(data)
    # Преобразуем их в 4 байта (uint32)
    size_bytes = size.to_bytes(4, byteorder='little')
    # И добавляем в начало данных для шифрования
    data = size_bytes + data
    # Убеждаемся, что данные имеют правильный размер блока для AES
    padded_data = pad(data, AES.block_size)
    # Создаем объект шифрования
    # Создаем вектор инициализации, состоящий из нулей
    iv = bytes([0] * 8)
    cipher = AES.new(key, AES.MODE_CTR, nonce=iv)
    # Получаем вектор инициализации и шифруем данные
    encrypted_data = cipher.encrypt(padded_data)
    # Возвращаем зашифрованные данные
    return encrypted_data


def print_data_as_var(title, data, size):
    if len(data) < size:
        data = data + b'\x00' * (size - len(data))
    print(f"var {title} = []byte{{")
    for i, b in enumerate(data):
        if i % 16 == 0:
            if i != 0:
                print()
            print("    ", end="")
        print(f"0x{b:02X}, ", end="")
    print("\n}")
        
    
def main(app_path, config_path, res_dir, show_golang_vars=False):
    print(f"App: {app_path}")
    print(f"Config: {config_path}\n")
    
    config_data = file_get_data(config_path)
    if not config_data:
        return
    
    key = get_random_key(16)
    config_data = encrypt_data(config_data, key)

    if show_golang_vars:
        print_data_as_var("configKey", key, 16)
        print()
        print_data_as_var("configData", config_data, 1024*2)
        print()

    file_list = file_get_list(app_path)
    if not file_list:
        return
    
    for app_path in file_list:
        print(f"Processing: {os.path.basename(app_path)}")
        
        file_data = file_get_data(app_path)
        if not file_data:
            return

        key_index = find_pattern(file_data, KEY_PATTERN)
        if key_index == -1:
            return
        
        config_index = find_pattern(file_data, CONFIG_PATTERN)
        if config_index == -1:
            return

        file_data[key_index:key_index + len(key)] = key
        file_data[config_index:config_index + len(config_data)] = config_data

        filename = os.path.basename(app_path)
        result_file = os.path.abspath(os.path.join(res_dir, filename))

        if not file_save(result_file, file_data):
            print(f"Failed to save file to {result_file}")
    
    print(f'\nResult: {res_dir}\n')


def first_file_in_folder(folder_path):
    import os
    files = os.listdir(folder_path)
    if files:
        return os.path.join(folder_path, files[0])
    else:
        print(f"No files found in {folder_path}")
        return None
    

def file_get_list(folder_path):
    import os
    files = os.listdir(folder_path)
    if files:
        return [os.path.join(folder_path, file) for file in files]
    else:
        print(f"No files found in {folder_path}")
        return []
    

if __name__ == '__main__':
    print("Builder v1.0\n")
    
    bin_dir = 'bin'
    config_path = 'config/config.json'
    res_dir = f'result/{BUILD_DATE}'
    show_golang_vars = True
    
    main(bin_dir, config_path, res_dir, show_golang_vars)