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

# Publish an already-built release only when it is newer than the Hub feed.
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ARTIFACT_DIR="${UPDATE_ARTIFACT_DIR:-$REPO_ROOT/codelanguage/artifacts/updateddata}"
UPDATE_SERVER_DIR="${UPDATE_SERVER_DIR:-$REPO_ROOT/codelanguage/artifacts/updateddata}"
LEGACY_UPDATE_DIR="${LEGACY_UPDATE_DIR:-}"
BASE_URL="${BASE_URL:-https://craftplusplus.216.128.145.79.nip.io/updateddata}"
PUBLIC_VERIFY="${PUBLIC_VERIFY:-auto}"

fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
[[ "$PUBLIC_VERIFY" == "auto" || "$PUBLIC_VERIFY" == "true" || "$PUBLIC_VERIFY" == "false" ]] || fail "PUBLIC_VERIFY must be auto, true, or false."
[[ -d "$ARTIFACT_DIR" ]] || fail "Update artifact directory does not exist: $ARTIFACT_DIR"
[[ -d "$UPDATE_SERVER_DIR" ]] || fail "Hub update directory does not exist: $UPDATE_SERVER_DIR"
command -v python3 >/dev/null || fail "python3 is required."
command -v sha256sum >/dev/null || fail "sha256sum is required."
command -v curl >/dev/null || fail "curl is required to verify the public feed."

if [[ -z "$LEGACY_UPDATE_DIR" && "$UPDATE_SERVER_DIR" == "$REPO_ROOT/codelanguage/artifacts/updateddata" && -d /root/Desktop/updateddata ]]; then
  LEGACY_UPDATE_DIR=/root/Desktop/updateddata
fi
if [[ -n "$LEGACY_UPDATE_DIR" && ! -d "$LEGACY_UPDATE_DIR" ]]; then
  fail "Legacy update directory does not exist: $LEGACY_UPDATE_DIR"
fi

RESULT_FILE="$(mktemp)"
python3 - "$ARTIFACT_DIR" "$UPDATE_SERVER_DIR" "$LEGACY_UPDATE_DIR" "$BASE_URL" "$RESULT_FILE" <<'PY'
import hashlib
import json
import pathlib
import re
import sys
import shutil
import urllib.parse
import zipfile

artifact_dir = pathlib.Path(sys.argv[1])
hub_dir = pathlib.Path(sys.argv[2])
legacy_text = sys.argv[3]
base_url = sys.argv[4].rstrip("/")
result_file = pathlib.Path(sys.argv[5])
legacy_dir = pathlib.Path(legacy_text) if legacy_text else None

version_pattern = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
def version_key(value):
    match = version_pattern.fullmatch(str(value))
    if not match:
        raise SystemExit(f"Invalid release version: {value!r}")
    return tuple(map(int, match.groups()))

feed_path = artifact_dir / "version.json"
if not feed_path.is_file():
    raise SystemExit(f"Missing update feed: {feed_path}")
feed = json.loads(feed_path.read_text(encoding="utf-8"))
candidate_version = str(feed.get("version", ""))
candidate_key = version_key(candidate_version)
archive_url = feed.get("updateArchiveUrl")
if not isinstance(archive_url, str):
    raise SystemExit("The update feed has no updateArchiveUrl.")
parsed = urllib.parse.urlparse(archive_url)
if not archive_url.startswith(base_url + "/"):
    raise SystemExit(f"Archive URL is not under the configured Hub URL: {archive_url}")
archive_name = pathlib.PurePosixPath(parsed.path).name
if not archive_name or archive_name in {".", ".."}:
    raise SystemExit("The update archive URL has an invalid filename.")
archive_path = artifact_dir / archive_name
if not archive_path.is_file():
    raise SystemExit(f"Archive named by version.json is missing: {archive_path}")
expected_hash = str(feed.get("updateArchiveSha256", "")).lower()
actual_hash = hashlib.sha256(archive_path.read_bytes()).hexdigest()
if not re.fullmatch(r"[0-9a-f]{64}", expected_hash) or actual_hash != expected_hash:
    raise SystemExit(f"Archive SHA-256 does not match version.json (actual {actual_hash}).")
with zipfile.ZipFile(archive_path) as archive:
    names = {name.lower() for name in archive.namelist()}
    if "codelanguage.exe" not in names:
        raise SystemExit("Update archive is missing codelanguage.exe.")
    if "updateinstaller/craftupdateinstaller.exe" not in names:
        raise SystemExit("Update archive is missing UpdateInstaller/CraftUpdateInstaller.exe.")

def live_version(directory):
    path = directory / "version.json"
    if not path.is_file():
        return None
    try:
        value = json.loads(path.read_text(encoding="utf-8")).get("version")
        return (str(value), version_key(value))
    except (OSError, json.JSONDecodeError, SystemExit):
        return None

current = live_version(hub_dir)
if current and candidate_key <= current[1]:
    result_file.write_text(f"SKIPPED: artifact {candidate_version} is not newer than Hub version {current[0]}.")
    raise SystemExit(0)

destinations = [hub_dir]
if legacy_dir and legacy_dir != hub_dir:
    destinations.append(legacy_dir)
for destination in destinations:
    if not destination.is_dir():
        raise SystemExit(f"Update destination does not exist: {destination}")

# Put the archive in place at every destination before switching any feed.
for destination in destinations:
    staged = destination / (archive_name + ".uploading")
    shutil.copyfile(archive_path, staged)
    staged.replace(destination / archive_name)
for destination in destinations:
    staged = destination / "version.json.uploading"
    shutil.copyfile(feed_path, staged)
    staged.replace(destination / "version.json")

for destination in destinations:
    copied_hash = hashlib.sha256((destination / archive_name).read_bytes()).hexdigest()
    copied_feed = json.loads((destination / "version.json").read_text(encoding="utf-8"))
    if copied_hash != expected_hash or copied_feed.get("version") != candidate_version:
        raise SystemExit(f"Post-upload verification failed in {destination}.")
result_file.write_text(f"UPLOADED: {candidate_version} {archive_name} {expected_hash}")
PY
UPLOAD_RESULT="$(cat "$RESULT_FILE")"
rm -f "$RESULT_FILE"
printf '%s\n' "$UPLOAD_RESULT"
[[ "$UPLOAD_RESULT" == UPLOADED:* ]] || exit 0

if [[ "$PUBLIC_VERIFY" == "false" ]]; then
  exit 0
fi

PUBLIC_FEED="$(mktemp)"
PUBLIC_ARCHIVE="$(mktemp)"
trap 'rm -f "$PUBLIC_FEED" "$PUBLIC_ARCHIVE"' EXIT
if ! curl --fail --silent --show-error --location --max-time 20 \
    -H 'Cache-Control: no-cache, no-store' "$BASE_URL/version.json" -o "$PUBLIC_FEED" 2>/dev/null; then
  if [[ "$PUBLIC_VERIFY" == "true" ]]; then
    fail "Uploaded locally, but public update feed is unreachable: $BASE_URL/version.json"
  fi
  printf 'Public feed is unreachable from this host; local Hub directory was verified.\n'
  exit 0
fi

python3 -c 'import json,sys; feed=json.load(open(sys.argv[1], encoding="utf-8")); _,version,_,digest=sys.argv[2].split(); actual=str(feed.get("updateArchiveSha256", "")).lower(); feed.get("version")==version or sys.exit("Public feed reports "+repr(feed.get("version")) + ", expected " + version); actual==digest or sys.exit("Public feed archive digest does not match the uploaded artifact."); print("Public feed reports update " + version + ".")' "$PUBLIC_FEED" "$UPLOAD_RESULT"

ARCHIVE_NAME="$(python3 -c 'import json,sys,urllib.parse; print(urllib.parse.urlparse(json.load(open(sys.argv[1]))["updateArchiveUrl"]).path.rsplit("/",1)[-1])' "$PUBLIC_FEED")"
curl --fail --silent --show-error --location --max-time 600 "$BASE_URL/$ARCHIVE_NAME" -o "$PUBLIC_ARCHIVE"
PUBLIC_HASH="$(sha256sum "$PUBLIC_ARCHIVE" | awk '{print $1}')"
EXPECTED_HASH="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["updateArchiveSha256"].lower())' "$PUBLIC_FEED")"
[[ "$PUBLIC_HASH" == "$EXPECTED_HASH" ]] || fail "Public archive SHA-256 does not match its feed."
printf 'Public update archive verified: %s\n' "$ARCHIVE_NAME"
