import sys, tempfile
from pathlib import Path
import ctypes
try:
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
except Exception as e:
    raise RuntimeError("cryptography package required. pip install cryptography") from e

PROJECT_ROOT = Path(__file__).parent.resolve()
ASSETS_DIR = PROJECT_ROOT / "assets"
TEMP_DIR = Path(tempfile.gettempdir()) / "OrvixGl_runtime"

FALLBACK_EMBEDDED_KEY_HEX = "8e9ec6c0f96ac0f61048a4a742d4db91e75313f7a315452216898ee5cad7c782"

def discover_keys():
    keys = []
    keyfile = ASSETS_DIR / "orvix_key.hex"
    if keyfile.exists():
        t = keyfile.read_text().strip()
        if len(t) >= 64:
            keys.append(t[:64])
    for kp in ASSETS_DIR.glob("*.dll.enc.key.txt"):
        t = kp.read_text().strip()
        if len(t) >= 64:
            keys.append(t[:64])
    keys.append(FALLBACK_EMBEDDED_KEY_HEX)
    return keys

def decrypt_file(enc, key_hex, out_path):
    key = bytes.fromhex(key_hex)
    data = enc.read_bytes()
    if len(data) < 13:
        raise ValueError("encrypted file too small / corrupted")
    nonce, ct = data[:12], data[12:]
    plain = AESGCM(key).decrypt(nonce, ct, None)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_bytes(plain)
    return out_path

def load_native(path: Path):
    if sys.platform.startswith("win"):
        return ctypes.WinDLL(str(path))
    return ctypes.CDLL(str(path))

def prepare_and_load(verbose=True):
    TEMP_DIR.mkdir(parents=True, exist_ok=True)
    enc_files = sorted(ASSETS_DIR.glob("*.dll.enc"))
    if verbose:
        print(f"Found {len(enc_files)} encrypted dll(s)")
    loaded = {}
    keys = discover_keys()
    for enc in enc_files:
        ok = False
        last = None
        for k in keys:
            try:
                out = TEMP_DIR / enc.stem.replace(".enc","")
                decrypt_file(enc, k, out)
                try:
                    lib = load_native(out)
                except Exception:
                    lib = None
                loaded[enc.name] = (lib, out)
                ok = True
                break
            except Exception as e:
                last = e
        if not ok and verbose:
            print(f"[ERROR] Could not decrypt {enc.name}: {last}")
    return loaded

def try_call_init(loaded):
    for name, (lib, path) in loaded.items():
        if lib is None:
            continue
        try:
            if hasattr(lib, "orvix_init"):
                lib.orvix_init.restype = ctypes.c_int
                res = lib.orvix_init()
                print(f"{name} -> orvix_init() -> {res}")
        except Exception as e:
            print(f"{name} init call failed: {e}")