#!/usr/bin/env python3
"""Rebuild the Create Aeronautics .mrpack from a live Prism instance.

Resolves every mod jar in the instance's mods/ folder to its Modrinth version
by SHA-1 hash (falling back to a manual CurseForge entry for aero-shadow-fix),
then packs modrinth.index.json + overrides into a Prism-importable .mrpack.
"""
import hashlib, json, os, subprocess, sys, tempfile, zipfile, urllib.request

USER_AGENT = "opencode-pack-builder/1.0"
INSTANCE = os.path.expanduser(
    "~/.local/share/PrismLauncher/instances/"
    "Create Aeronautics - Airship & Expansion/.minecraft"
)
MODS_DIR = os.path.join(INSTANCE, "mods")
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                   "ReapersSky-1.0.0.mrpack")

# (name in instance, override source path) — shipped as-is into .minecraft
OVERRIDES = [
    ("config/flywheel-client.toml",   os.path.join(INSTANCE, "config/flywheel-client.toml")),
    ("config/iris.properties",        os.path.join(INSTANCE, "config/iris.properties")),
    ("config/irisflw-client.toml",    os.path.join(INSTANCE, "config/irisflw-client.toml")),
    ("resourcepacks/FreshAnimations_v1.10.4.zip", os.path.join(INSTANCE, "resourcepacks/FreshAnimations_v1.10.4.zip")),
    ("shaderpacks/ComplementaryReimagined_r5.8.1 + EuphoriaPatches_1.9.3",
     os.path.join(INSTANCE, "shaderpacks/ComplementaryReimagined_r5.8.1 + EuphoriaPatches_1.9.3")),
]

# CurseForge-only mod: project 1641969 (aero-shadow-fix), file 8589529.
CF_FILES = {
    "aero-shadow-fix.jar": {
        "url": "https://www.curseforge.com/api/v1/mods/1641969/files/8589529/download",
    },
}

def sha1(path):
    h = hashlib.sha1()
    with open(path, "rb") as f:
        for block in iter(lambda: f.read(65536), b""):
            h.update(block)
    return h.hexdigest()

def resolve(sha):
    url = f"https://api.modrinth.com/v2/version_file/{sha}?algorithm=sha1"
    req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
    with urllib.request.urlopen(req, timeout=20) as r:
        d = json.load(r)
    return d.get("id", ""), d.get("files", [{}])[0].get("url", "")

def main():
    files = []
    for fname in sorted(os.listdir(MODS_DIR)):
        if not fname.endswith(".jar"):
            continue
        path = os.path.join(MODS_DIR, fname)
        fsize = os.path.getsize(path)
        if fname in CF_FILES:
            url = CF_FILES[fname]["url"]
        else:
            vid, url = resolve(sha1(path))
            if not url:
                print(f"!! could not resolve {fname} — add to CF_FILES and rerun")
                sys.exit(1)
        files.append({
            "path": "mods/" + fname,
            "hashes": {"sha1": sha1(path)},
            "env": {"client": "required", "server": "optional"},
            "downloads": [url],
            "fileSize": fsize,
        })

    index = {
        "formatVersion": 1,
        "game": "minecraft",
        "versionId": "1.0.0",
        "name": "Reaper's Sky",
        "summary": "Airships, exploration & tech. 75 mods for Minecraft 1.21.1 / NeoForge.",
        "files": files,
        "dependencies": {"minecraft": "1.21.1", "neoforge": "21.1.248"},
    }

    with tempfile.TemporaryDirectory() as tmp:
        with open(os.path.join(tmp, "modrinth.index.json"), "w") as f:
            json.dump(index, f, indent=2)
        ov = os.path.join(tmp, "overrides")
        for name, src in OVERRIDES:
            dst = os.path.join(ov, name)
            if os.path.isdir(src):
                os.makedirs(dst, exist_ok=True)
                for root, _, fnames in os.walk(src):
                    for fn in fnames:
                        sp = os.path.join(root, fn)
                        dp = os.path.join(dst, os.path.relpath(sp, src))
                        os.makedirs(os.path.dirname(dp), exist_ok=True)
                        with open(sp, "rb") as si, open(dp, "wb") as di:
                            di.write(si.read())
            else:
                os.makedirs(os.path.dirname(dst), exist_ok=True)
                with open(src, "rb") as si, open(dst, "wb") as di:
                    di.write(si.read())
        with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as z:
            z.write(os.path.join(tmp, "modrinth.index.json"), "modrinth.index.json")
            for root, _, fnames in os.walk(ov):
                for fn in fnames:
                    sp = os.path.join(root, fn)
                    z.write(sp, os.path.relpath(sp, tmp))

    print(f"Wrote {OUT} ({len(files)} mods, "
          f"{os.path.getsize(OUT)//1024//1024} MB)")

if __name__ == "__main__":
    main()