"""حزمة الموديول (.vmod = zip موقّع) — تعبئة/تحقق/تحميل ساخن.

الحزمة: manifest.json (+ signature) + detector.py + نموذج اختياري.
الهاش الحتمي = sha256(manifest بلا التوقيع + محتوى الملفات مرتّبة)، والتوقيع Ed25519 عليه.
"""
import base64
import hashlib
import importlib.util
import io
import json
import os
import sys
import zipfile

from . import signing
from .api import Detector


def _digest(manifest_no_sig: dict, files: dict) -> bytes:
    h = hashlib.sha256()
    h.update(json.dumps(manifest_no_sig, sort_keys=True,
                        ensure_ascii=False).encode())
    for name in sorted(files):                       # ترتيب حتمي
        h.update(name.encode())
        h.update(files[name])
    return h.digest()


def pack(manifest: dict, files: dict, priv: bytes) -> bytes:
    """يبني .vmod موقّعاً. manifest: {key, version, kind, entry, triggers, ...}."""
    man = {k: v for k, v in manifest.items() if k != "signature"}
    sig = signing.sign(priv, _digest(man, files))
    man["signature"] = base64.b64encode(sig).decode()
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
        z.writestr("manifest.json", json.dumps(man, ensure_ascii=False))
        for name, data in files.items():
            z.writestr(name, data)
    return buf.getvalue()


def verify_package(vmod: bytes, pub: bytes) -> dict:
    """يتحقق التوقيع ويرجع {manifest, files}. يرمي ValueError عند الرفض."""
    with zipfile.ZipFile(io.BytesIO(vmod)) as z:
        man = json.loads(z.read("manifest.json"))
        files = {n: z.read(n) for n in z.namelist() if n != "manifest.json"}
    sig_b64 = man.pop("signature", "")
    if not sig_b64:
        raise ValueError("حزمة بلا توقيع — مرفوضة")
    if not signing.verify(pub, _digest(man, files), base64.b64decode(sig_b64)):
        raise ValueError("توقيع غير صالح — الحزمة معدّلة أو غير موثوقة")
    return {"manifest": man, "files": files}


def install(vmod: bytes, pub: bytes, modules_dir: str) -> dict:
    """يتحقق ثم يفكّ الحزمة في modules_dir/<key>/v<version>/. يرجع manifest + المسارات."""
    pkg = verify_package(vmod, pub)
    man = pkg["manifest"]
    dest = os.path.join(modules_dir, man["key"], f"v{man['version']}")
    os.makedirs(dest, exist_ok=True)
    for name, data in pkg["files"].items():
        with open(os.path.join(dest, os.path.basename(name)), "wb") as f:
            f.write(data)
    man["_dir"] = dest
    man["_entry"] = os.path.join(dest, man.get("entry", "detector.py"))
    if man.get("model"):
        man["_model"] = os.path.join(dest, os.path.basename(man["model"]))
    return man


def load_detector_class(entry_path: str, key: str, version):
    """تحميل ساخن: استيراد باسم نسخة فريد → صنف Detector (نسخة لكل كاميرا)."""
    name = f"veedon_mod_{key}_v{version}"
    spec = importlib.util.spec_from_file_location(name, entry_path)
    mod = importlib.util.module_from_spec(spec)
    sys.modules[name] = mod
    spec.loader.exec_module(mod)                      # يشغّل الكود (بعد التحقق فقط)
    for obj in vars(mod).values():
        if isinstance(obj, type) and issubclass(obj, Detector) and obj is not Detector:
            return obj
    raise ValueError(f"لا صنف Detector في {entry_path}")


def load_detector(entry_path: str, key: str, version) -> Detector:
    return load_detector_class(entry_path, key, version)()
