#!/usr/bin/env python3
"""Regenerate server/mods.json from the Prism instance's mods directory.

For every .jar in the pack instance it:
  - computes sha1
  - looks up the Modrinth download URL via the version_file API
    (aero-shadow-fix is CurseForge-only, pinned manually)
  - drops the 19 client-only mods
and writes the server manifest to mods.json.

Usage:  python3 build_mods.py           regenerate server/mods.json
        python3 build_mods.py --push    regenerate, then update the live server
                                        (stop -> sync mods -> start, via SSH)
"""
import argparse, hashlib, json, os, subprocess, sys, time

INSTANCE = os.path.expanduser(
    "~/.local/share/PrismLauncher/instances/Reaper's Sky - dev/.minecraft/mods")
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mods.json")
UPDATE_SH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "update.sh")
SERVER_HOST = os.environ.get("RS_SERVER_HOST", "root@ReapersSky")
MC_VERSION = "1.21.1"
NEOFORGE_VERSION = "21.1.248"

CLIENT_ONLY = {
    "iris-neoforge-1.8.14-beta.1+mc1.21.1.jar",
    "EuphoriaPatcher-1.9.3-r5.8.1-neoforge.jar",
    "iris-flywheel-compat-NeoForge-2.3.1.jar",
    "sodium-neoforge-0.8.12+mc1.21.1.jar",
    "moreculling-neoforge-1.21.1-1.0.9.jar",
    "ImmediatelyFast-NeoForge-1.6.12+1.21.1.jar",
    "entityculling-neoforge-1.10.5-mc1.21.1.jar",
    "dynamic-fps-3.11.4+mc1.21.0-neoforge.jar",
    "MouseTweaks-neoforge-mc1.21-2.26.1.jar",
    "appleskin-neoforge-mc1.21-3.0.9.jar",
    "Controlling-neoforge-1.21.1-19.0.5.jar",
    "Searchables-neoforge-1.21.1-1.0.2.jar",
    "skinlayers3d-neoforge-1.11.2-mc1.21.1.jar",
    "xaerominimap-neoforge-1.21.1-26.4.2.jar",
    "xaeroworldmap-neoforge-1.21.1-1.44.2.jar",
    "jade-sable-compat-1.3.0.jar",
    "aero-shadow-fix.jar",
    "aeronautics-propeller-blur-1.0a.jar",
}

CURSEFORGE_ONLY = {
    "aero-shadow-fix.jar": "https://www.curseforge.com/api/v1/mods/1641969/files/8589529/download",
    "arssophisticatedcompat-0.3.0.jar": "https://www.curseforge.com/api/v1/mods/1653477/files/8653384/download",
}

UA = "opencode-pack-builder/1.0"


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


def resolve(jar, digest):
    if jar in CURSEFORGE_ONLY:
        return CURSEFORGE_ONLY[jar]
    r = subprocess.run(
        ["curl", "-s", "--max-time", "20", "-H", f"User-Agent: {UA}",
         f"https://api.modrinth.com/v2/version_file/{digest}?algorithm=sha1"],
        capture_output=True, text=True)
    try:
        return json.loads(r.stdout)["files"][0]["url"]
    except Exception:
        return ""


def push():
    """Upload mods.json to the server and run update.sh there (stop/sync/start)."""
    print(f"Uploading manifest to {SERVER_HOST}:/tmp/reaper-sky-mods.json ...")
    with open(OUT, "rb") as f:
        subprocess.run(["ssh", SERVER_HOST, "cat > /tmp/reaper-sky-mods.json"],
                       stdin=f, check=True)
    print("Running update.sh on the server (stop -> sync -> start) ...")
    with open(UPDATE_SH, "rb") as f:
        subprocess.run(["ssh", SERVER_HOST, "bash -s -- /tmp/reaper-sky-mods.json"],
                       stdin=f, check=True)


def main():
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument("--push", action="store_true",
                        help="also update the live server: stop it, sync mods "
                             "(prune + download + sha1-verify), start it again")
    args = parser.parse_args()

    mods = []
    for jar in sorted(os.listdir(INSTANCE)):
        if not jar.endswith(".jar"):
            continue
        if jar in CLIENT_ONLY:
            print(f"  skip (client-only): {jar}")
            continue
        digest = sha1(os.path.join(INSTANCE, jar))
        url = resolve(jar, digest)
        if not url:
            print(f"  !! no URL for {jar} -- fix manually", file=sys.stderr)
        mods.append({"file": jar, "url": url, "sha1": digest})
        time.sleep(0.1)
    manifest = {"minecraft": MC_VERSION, "neoforge": NEOFORGE_VERSION, "mods": mods}
    with open(OUT, "w") as f:
        json.dump(manifest, f, indent=2)
    print(f"Wrote {len(mods)} server mods to {OUT}")
    if args.push:
        push()


if __name__ == "__main__":
    main()