from pathlib import Path
from base64 import b64encode
from httpx import Client
from os import urandom
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes


PASSWORD: str = input("Password: ")


ENCODING: str = "utf-8"

current_dirpath: Path = Path(__file__).resolve().parent
dist_dirpath: Path = current_dirpath / "dist"
http_client: Client = Client()


if not dist_dirpath.exists():
    dist_dirpath.mkdir()


def get_encryption_key(iv: bytes, password: str) -> bytes:
    return PBKDF2HMAC(
        algorithm = hashes.SHA256(),
        length = 32,
        salt = iv,
        iterations = 100000,
        backend = default_backend()
    ).derive(password.encode(ENCODING))

def encrypt_js(js_code: str) -> str:
    iv: bytes = urandom(16)

    key: bytes = get_encryption_key(iv, PASSWORD)

    cipher: Cipher = Cipher(
        algorithm = algorithms.AES(key),
        mode = modes.CBC(iv),
        backend = default_backend()
    )

    encryptor = cipher.encryptor()

    pad_length: int = 16 - len(js_code) % 16
    padded_message: str = js_code + chr(pad_length) * pad_length

    encrypted_data = encryptor.update(padded_message.encode(ENCODING)) + encryptor.finalize()

    return b64encode(iv + encrypted_data).decode(ENCODING)


def minify_js(js_code: str) -> str:
    response = http_client.post(
        "https://www.toptal.com/developers/javascript-minifier/api/raw",
        data = {
            "input": js_code
        }
    )

    return response.text


def inherit_saver(file_paths: list[tuple[Path, bool]]) -> None:
    if len(file_paths) < 2:
        print("Not enough files provided for hierarchy encoding.")
        return

    file_paths.reverse()

    for index, (file_path, _) in enumerate(file_paths):
        if index == 0:
            (dist_dirpath / file_path.name).write_text(file_path.read_text(ENCODING))
            continue

        encoded_file_content: str = b64encode(
            minify_js((dist_dirpath / file_paths[index - 1][0].name).read_text(ENCODING)).encode(ENCODING)
        ).decode(ENCODING)

        (dist_dirpath / file_path.name).write_text(
            file_path.read_text(ENCODING).replace(
                ".....",
                (
                    encrypt_js(encoded_file_content)
                    if file_paths[index - 1][1]
                    else
                    encoded_file_content
                )
            )
        )

    file_path = file_paths[-1][0]

    (dist_dirpath / (file_path.name + ".txt")).write_text(b64encode((dist_dirpath / file_path.name).read_text(ENCODING).encode(ENCODING)).decode(ENCODING))

    print(f"All files saved to [{dist_dirpath}]: {[file_path[0].name for file_path in file_paths]}")


if __name__ == "__main__":
    inherit_saver([
        # (current_dirpath / "all.js", False),
        # (current_dirpath / "main.js", True),
        # (current_dirpath / "submain.js", False),
        # (current_dirpath / "inject_script.js", False),
        # (current_dirpath / "main_new.js", False),
        # (current_dirpath / "inject_script_new.js", True),
        (current_dirpath / "main_new2.js", True),
        (current_dirpath / "inject_script_new2.js", True),
    ])
