#!/usr/bin/env bash
set -euo pipefail

PROJECT_DIR="${1:-.}"
ASSETS="$PROJECT_DIR/assets"
APPS_JSON="$ASSETS/apps.json"
PLATFORM_SDK_INT="${PLATFORM_SDK_INT:-36}"

[[ -f "$APPS_JSON" ]] || {
    echo "apps.json not found: $APPS_JSON" >&2
    exit 1
}

APKSIGNER="${APKSIGNER:-$(command -v apksigner || true)}"
[[ -n "$APKSIGNER" ]] || {
    echo "apksigner not found; set APKSIGNER" >&2
    exit 1
}

AAPT="${AAPT:-$(command -v aapt || true)}"
[[ -n "$AAPT" ]] || AAPT="$(command -v aapt2 || true)"
[[ -n "$AAPT" ]] || {
    echo "aapt/aapt2 not found; set AAPT" >&2
    exit 1
}

python3 - \
    "$APPS_JSON" \
    "$ASSETS" \
    "$APKSIGNER" \
    "$AAPT" \
    "$PLATFORM_SDK_INT" <<'PY'

import hashlib
import json
import os
import pathlib
import re
import subprocess
import sys

json_path = pathlib.Path(sys.argv[1])
assets = pathlib.Path(sys.argv[2])
apksigner = sys.argv[3]
aapt = sys.argv[4]
platform_sdk = int(sys.argv[5])

data = json.loads(json_path.read_text())
missing = []


def select_signer_sha256(output: str, apk_name: str) -> str:
    # Handles SDK-targeted signing output such as Signal:
    # Signer (minSdkVersion=33, maxSdkVersion=2147483647) certificate SHA-256 digest: ...
    targeted = re.findall(
        r"Signer\s+\(minSdkVersion=(\d+),\s*maxSdkVersion=(\d+)\)"
        r"\s+certificate SHA-256 digest:\s*([0-9a-fA-F:]+)",
        output,
    )

    applicable = []
    for min_raw, max_raw, digest in targeted:
        min_sdk = int(min_raw)
        max_sdk = int(max_raw)
        if min_sdk <= platform_sdk <= max_sdk:
            applicable.append(
                (min_sdk, max_sdk, digest.replace(":", "").lower())
            )

    if applicable:
        # Prefer the applicable signer with the highest minimum SDK.
        applicable.sort(key=lambda item: item[0], reverse=True)
        return applicable[0][2]

    # Handles standard apksigner output:
    # Signer #1 certificate SHA-256 digest: ...
    normal = re.search(
        r"Signer(?:\s+#\d+)?\s+certificate SHA-256 digest:"
        r"\s*([0-9a-fA-F:]+)",
        output,
    )
    if normal:
        return normal.group(1).replace(":", "").lower()

    raise SystemExit(
        f"Could not determine applicable signer SHA-256 "
        f"for {apk_name} on SDK {platform_sdk}"
    )


for app in data.get("apps", []):
    apk = assets / app["apk"]

    if not apk.is_file():
        missing.append(apk.name)
        continue

    # APK file hash.
    digest = hashlib.sha256()
    with apk.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    apk_sha256 = digest.hexdigest()

    # Verify APK signature before using signer metadata.
    cp = subprocess.run(
        [apksigner, "verify", "--print-certs", str(apk)],
        text=True,
        capture_output=True,
    )
    if cp.returncode != 0:
        raise SystemExit(
            f"APK signature verification FAILED: {apk.name}\n"
            f"{cp.stdout}\n{cp.stderr}"
        )

    signer_sha256 = select_signer_sha256(
        cp.stdout + "\n" + cp.stderr,
        apk.name,
    )

    # Package + version metadata.
    cp = subprocess.run(
        [aapt, "dump", "badging", str(apk)],
        text=True,
        capture_output=True,
    )
    if cp.returncode != 0:
        raise SystemExit(
            f"Could not read APK metadata: {apk.name}\n"
            f"{cp.stdout}\n{cp.stderr}"
        )

    match = re.search(
        r"package:\s+name='([^']+)'\s+"
        r"versionCode='([^']+)'\s+"
        r"versionName='([^']*)'",
        cp.stdout,
    )
    if not match:
        raise SystemExit(
            f"Could not read package/version metadata: {apk.name}"
        )

    package_name, version_code_raw, version_name = match.groups()

    if package_name != app["package"]:
        raise SystemExit(
            f"Package mismatch for {apk.name}: "
            f"expected {app['package']}, got {package_name}"
        )

    try:
        version_code = int(version_code_raw)
    except ValueError:
        raise SystemExit(
            f"Invalid versionCode for {apk.name}: {version_code_raw}"
        )

    app["apkSha256"] = apk_sha256
    app["signerSha256"] = signer_sha256
    app["versionCode"] = version_code
    app["versionName"] = version_name

    display_version = (
        version_name if version_name.lower().startswith("v")
        else "v" + version_name
    )
    print(
        f"{app['name']}: {display_version} ({version_code}) "
        f"signer={signer_sha256[:16]}..."
    )

if missing:
    raise SystemExit(
        "Missing offline APK assets; metadata NOT written: "
        + ", ".join(missing)
    )

tmp = json_path.with_suffix(json_path.suffix + ".tmp")
tmp.write_text(
    json.dumps(data, indent=2, ensure_ascii=False) + "\n"
)
os.replace(tmp, json_path)

print(
    f"\nAll offline APKs verified for Android SDK {platform_sdk}."
)
PY

echo "Verified and updated atomically: $APPS_JSON"
