#!/bin/bash
# fix-curseforge-export.sh
# Post-processes a packwiz CurseForge export ZIP to fix API-excluded mods.
# These mods exist on CurseForge but are excluded from the 3rd-party API,
# so packwiz bundles them as JARs instead of referencing them in manifest.json.
# CurseForge rejects bundled JARs for mods that have CurseForge projects.
#
# This script removes the bundled JARs and adds manifest.json references instead.

set -euo pipefail

if [ $# -lt 1 ]; then
    echo "Usage: $0 <export.zip>"
    exit 1
fi

ZIP="$1"

if [ ! -f "$ZIP" ]; then
    echo "Error: $ZIP not found"
    exit 1
fi

# API-excluded mods: projectID:fileID:jarname
#
# CurseForge rejects uploads that bundle JARs for mods that have CurseForge
# projects. Do not keep CF-hosted mods in overrides/mods for ATL compatibility:
# that may avoid ATL's pack-version lookup bug, but it causes CurseForge's own
# release validation to reject the file. CC:Tweaked is the historical exception
# because it is intentionally Modrinth-hosted in this pack.

# These mods are on CurseForge but excluded from the API download endpoint.
# Update these when mod versions change.
EXCLUDED_MODS=(
    "636540:7850172:Structory_26.1_v1.3.16.jar"
    "783522:7842394:Structory_Towers_26.1_v1.0.16.jar"
    "433760:8274908:notenoughanimations-neoforge-1.12.4-mc1.21.1.jar"
    "261251:8135209:bwncr-neoforge-1.21.1-3.20.4.jar"
    "568372:7449637:fish_of_thieves-mc1.21.1-v21.1.1.2-neoforge.jar"
    "844270:8510341:labellingcontainers-1.9.3.jar"
    "348521:5729127:cloth-config-15.0.140-neoforge.jar"
    "990853:5980402:fastconfigapi-2.1.0.jar"
    "1097945:5791555:void_lib-1.1.6.jar"
    "344906:5768073:betterwithminecolonies-1.20.0-1.21.1.jar"
    "1469650:7756701:ModularHubStylePack-1-21-1-v1.1.jar"
    "923238:8156977:Design-n-Decor-1.21.1-2.2b.jar"
    "1281770:8392676:fxntstorage-1.3.1+mc-1.21.1-neoforge.jar"
    "1549835:8238692:fluidic_tanks-1.1.1.jar"
    "676721:8240058:create-aeronautics-bundled-1.21.1-1.3.0.jar"
    "1532013:8030795:ballastmod-0.1.0.jar"
    "1523649:8010260:aeronautics_joystick3.0-1.21.1.jar"
    "1559009:8171965:aeroconnectedfluidvessel-1.0.0.jar"
    "1520961:7977632:aerocopycats-1.1.1.jar"
    "1532665:8032414:aeroadditionbridge-1.0.0.jar"
    "1586421:8328547:dndecorsable-1.0.3.jar"
    "1559550:8230211:shield_generators-1.3.0.jar"
    "1289504:8444609:create-shimmer-1.2.10.jar"
    "1601871:8560213:simulatedcoasters-0.1.2.jar"
)

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

# Guard against referencing deleted/invalid CurseForge files, which CurseForge
# rejects at upload time ("References file with invalid status: ... - Deleted").
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -x "$SCRIPT_DIR/validate-cf-files.sh" ]; then
    echo "Validating CurseForge file references..."
    if ! "$SCRIPT_DIR/validate-cf-files.sh"; then
        echo ""
        echo "ERROR: One or more CurseForge file references are invalid/deleted."
        echo "       CurseForge will reject this export. Fix them and re-export."
        exit 1
    fi
    echo ""
fi

# Guard against client-side mods requiring a newer NeoForge than the pack targets.
# The headless server test (test-pack.sh) does NOT load side="client" mods, so a
# client-only NeoForge-floor bump is invisible to it and crashes the CLIENT at
# launch (this is how JEI 19.42 shipped broken in v2.45.4). Static jar check.
if [ -x "$SCRIPT_DIR/validate-client-floors.sh" ]; then
    if ! "$SCRIPT_DIR/validate-client-floors.sh"; then
        echo ""
        echo "ERROR: One or more client-side mods require a newer NeoForge than the"
        echo "       pack targets. The server test cannot catch this — it would crash"
        echo "       the CLIENT at launch. Pin the offending mod(s) and re-export."
        exit 1
    fi
    echo ""
fi

echo "Fixing CurseForge export: $ZIP"

# Extract manifest.json
unzip -o -q "$ZIP" manifest.json -d "$TMPDIR"

# Add each excluded mod to manifest.json and remove bundled JAR
for entry in "${EXCLUDED_MODS[@]}"; do
    IFS=: read -r projectID fileID jarname <<< "$entry"

    # Check if this JAR is actually in the ZIP
    if unzip -l "$ZIP" "overrides/mods/$jarname" > /dev/null 2>&1; then
        echo "  Fixing: $jarname (project=$projectID, file=$fileID)"

        # Remove JAR from ZIP
        zip -d "$ZIP" "overrides/mods/$jarname" > /dev/null 2>&1 || true

        # Add to manifest.json
        python3 -c "
import json, sys
with open('$TMPDIR/manifest.json', 'r') as f:
    data = json.load(f)
# Check if already in manifest
existing = {f['projectID'] for f in data['files']}
if $projectID not in existing:
    data['files'].append({'projectID': $projectID, 'fileID': $fileID, 'required': True})
with open('$TMPDIR/manifest.json', 'w') as f:
    json.dump(data, f, indent=2)
"
    else
        echo "  Skipped: $jarname (not in ZIP)"
    fi
done

# Update manifest.json in the ZIP
ABSZIP="$(cd "$(dirname "$ZIP")" && pwd)/$(basename "$ZIP")"
(cd "$TMPDIR" && zip -q "$ABSZIP" manifest.json)

# Summary
FINAL_COUNT=$(unzip -p "$ZIP" manifest.json | python3 -c "import json,sys; print(len(json.load(sys.stdin)['files']))")
BUNDLED=$(unzip -l "$ZIP" | grep -c 'overrides/mods/.*\.jar' || true)
echo ""
echo "Done! manifest.json now has $FINAL_COUNT mod references, $BUNDLED bundled JARs remaining."

# --- Validate: check remaining bundled JARs against CurseForge API ---
# CurseForge rejects uploads that bundle JARs belonging to CF-hosted projects.
if [ "$BUNDLED" -gt 0 ]; then
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
    API_KEY=""
    if [ -f "$SCRIPT_DIR/key.txt" ]; then
        API_KEY=$(cat "$SCRIPT_DIR/key.txt")
    elif [ -f "key.txt" ]; then
        API_KEY=$(cat "key.txt")
    fi

    if [ -n "$API_KEY" ]; then
        echo ""
        echo "Validating remaining bundled JARs against CurseForge API..."
        REJECT=0
        while IFS= read -r jar; do
            jarname=$(basename "$jar")
            # Search CurseForge for this filename
            MATCH=$(curl -s -H "x-api-key: $API_KEY" \
                "https://api.curseforge.com/v1/fingerprints/432" \
                --data-raw "{\"fingerprints\":[]}" 2>/dev/null || true)
            # Simpler approach: search by mod name derived from jar filename
            searchterm=$(echo "$jarname" | sed 's/-neoforge.*//;s/-forge.*//;s/-[0-9].*//;s/_/-/g')
            RESULT=$(curl -s -H "x-api-key: $API_KEY" \
                "https://api.curseforge.com/v1/mods/search?gameId=432&searchFilter=$searchterm&classId=6&pageSize=3" 2>/dev/null)
            CF_MATCH=$(echo "$RESULT" | python3 -c "
import json, sys
try:
    data = json.load(sys.stdin)
    for mod in data.get('data', []):
        for f in mod.get('latestFilesIndexes', []):
            if '$jarname' in f.get('filename', ''):
                print(f'FOUND: {mod[\"name\"]} (project {mod[\"id\"]})')
                sys.exit(0)
except: pass
" 2>/dev/null)
            if [ -n "$CF_MATCH" ]; then
                echo "  ERROR: $jarname belongs to a CurseForge project — CurseForge will reject this upload!"
                echo "         $CF_MATCH"
                echo "         Add it to the EXCLUDED_MODS list in this script."
                REJECT=1
            fi
        done < <(unzip -l "$ZIP" | grep 'overrides/mods/.*\.jar' | awk '{print $NF}')

        if [ "$REJECT" -gt 0 ]; then
            echo ""
            echo "ERROR: CurseForge will reject this export. Fix the EXCLUDED_MODS list and re-run."
            exit 1
        else
            echo "  All remaining bundled JARs are non-CurseForge. Export is clean."
        fi
    else
        echo ""
        echo "WARNING: No key.txt found — cannot validate bundled JARs against CurseForge API."
        echo "         Manually verify that no bundled JARs belong to CurseForge-hosted projects."
    fi
fi
