"""Builder M13: turn the LGPL FFmpeg n7.1 install (m13_build_ffmpeg.sh) into drop-in copies of the seven FFmpeg dylibs
the face check's cv2 (opencv-python 4.14.0.94) bundles in cv2/.dylibs, after Builder PB's pb_prepare_mac_dylibs.py:
install ids /DLC/cv2/.dylibs/<name>, deps @loader_path/<name>, ad-hoc signed. One difference from PB: the wheel's cv2
links libavcodec.61.19.101.dylib (FFmpeg 7.1.1); n7.1's avcodec is 61.19.100 (same major and minor, so the same ABI),
so our avcodec is installed under the file name and id the wheel expects, libavcodec.61.19.101.dylib, and cv2.abi3.so
stays byte-identical to the PyPI wheel. Writes LGPL-FFMPEG-DYLIBS-face.json with the sha256 pins.
  python3 m13_prepare_dylibs.py <install dir> <out dir>"""
import hashlib, json, re, shutil, subprocess, sys
from pathlib import Path

SRC = {"avcodec": "libavcodec.61.19.100.dylib", "avformat": "libavformat.61.7.100.dylib", "avutil": "libavutil.59.39.100.dylib",
       "swscale": "libswscale.8.3.100.dylib", "swresample": "libswresample.5.3.100.dylib", "avdevice": "libavdevice.61.3.100.dylib",
       "avfilter": "libavfilter.10.4.100.dylib"}
DST = dict(SRC, avcodec="libavcodec.61.19.101.dylib")
OK = ("/usr/lib/", "/System/Library/", "@loader_path/")


def run(*a):
    return subprocess.run(a, check=True, capture_output=True, text=True).stdout


inst, out = Path(sys.argv[1]), Path(sys.argv[2])
if out.exists():
    shutil.rmtree(out)
out.mkdir(parents=True)
for k in SRC:
    shutil.copyfile(inst / "lib" / SRC[k], out / DST[k])
for k in SRC:
    p = out / DST[k]
    run("install_name_tool", "-id", f"/DLC/cv2/.dylibs/{DST[k]}", str(p))
    for ln in run("otool", "-L", str(p)).splitlines()[2:]:
        dep = ln.strip().split(" (")[0]
        m = re.match(r".*/lib(avcodec|avformat|avutil|swscale|swresample|avdevice|avfilter)\.[\d.]+dylib$", dep)
        if m and not dep.startswith("@loader_path/"):
            run("install_name_tool", "-change", dep, "@loader_path/" + DST[m.group(1)], str(p))
    run("codesign", "-f", "-s", "-", str(p))
pins = {}
for k in SRC:
    p = out / DST[k]
    deps = [ln.strip().split(" (")[0] for ln in run("otool", "-L", str(p)).splitlines()[2:]]
    bad = [d for d in deps if not d.startswith(OK)]
    assert not bad, (DST[k], bad)
    lic = re.findall(rb"(?:L?GPL version [^\x00]+|nonfree and unredistributable)", p.read_bytes())
    minos = re.findall(r"minos (\S+)", run("otool", "-l", str(p)))
    pins[DST[k]] = {"sha256": hashlib.sha256(p.read_bytes()).hexdigest(), "bytes": p.stat().st_size, "builtAs": SRC[k],
                    "id": run("otool", "-D", str(p)).splitlines()[-1], "deps": deps, "minos": minos,
                    "licence_strings": sorted({x.decode() for x in lic})}
cfg = re.search(rb"--prefix=[^\x00]+", (out / DST["avutil"]).read_bytes())
(out / "LGPL-FFMPEG-DYLIBS-face.json").write_text(json.dumps({"ffmpeg": "n7.1 b08d7969c550a804a59511c7b83f2dd8cc0499b8",
    "sourceTarSha256": "1ae1e22a5b7ef961570e187bdd1875a6b0fa42289a9c54c301a4a99cc2c3863d",
    "configure": cfg.group(0).decode() if cfg else None, "dylibs": pins}, indent=1) + "\n")
print(json.dumps({k: (v["sha256"][:16], v["minos"], v["licence_strings"]) for k, v in pins.items()}, indent=1))
