#!/usr/bin/env bash

set -Eeuo pipefail
IFS=$'\n\t'
umask 077

PROGRAM_NAME="tranzify-watch-bootstrap"
DEFAULT_BASE_URL="https://packages.tranzify.watch"
EMBEDDED_RELEASE_FINGERPRINT="50723055bf2bd9f5bc790bc8954acd31520c2bc8d3a7865a9b6bfb06c8541893"
BASE_URL="$DEFAULT_BASE_URL"
CHANNEL="stable"
VERSION=""
TRUSTED_RELEASE_KEY=""
EXPECTED_FINGERPRINT="$EMBEDDED_RELEASE_FINGERPRINT"
EXPECTED_DESCRIPTOR_SHA256=""
VERIFY_ONLY=false
ALLOW_DEVELOPMENT_RELEASE=false
INSTALLER_ARGUMENTS=()

fail() {
  printf '%s: %s\n' "$PROGRAM_NAME" "$1" >&2
  exit 1
}

progress() {
  [[ "${TRANZIFY_UPDATE_PROGRESS:-}" == "1" ]] || return 0
  local stage="$1"
  shift
  printf 'TRNZ_PROGRESS %s' "$stage"
  (($# == 0)) || printf ' %s' "$@"
  printf '\n'
}

require_value() {
  [[ $# -ge 2 && -n "${2:-}" ]] || fail "option $1 requires a value"
}

usage() {
  cat <<'EOF'
Usage: curl -fsS https://packages.tranzify.watch/install.sh | sudo bash

Options:
  --channel NAME               Signed release channel (default: stable)
  --version VERSION            Install an explicit immutable release
  --base-url HTTPS_URL         Package origin (default: packages.tranzify.watch)
  --trusted-release-key PATH   External trust key for controlled testing
  --expected-fingerprint HEX   Required with an external trust key
  --expected-descriptor-sha256 HEX
                               Require this exact signed descriptor
  --verify-only                Download and verify without installing
  --allow-development-release  Test-only; requires an external trusted key
  --                           Pass remaining options to the managed installer
  -h, --help                   Show this help
EOF
}

while (($# > 0)); do
  case "$1" in
    --channel) require_value "$@"; CHANNEL="$2"; shift 2 ;;
    --version) require_value "$@"; VERSION="$2"; shift 2 ;;
    --base-url) require_value "$@"; BASE_URL="$2"; shift 2 ;;
    --trusted-release-key) require_value "$@"; TRUSTED_RELEASE_KEY="$2"; shift 2 ;;
    --expected-fingerprint)
      require_value "$@"
      EXPECTED_FINGERPRINT="$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')"
      shift 2
      ;;
    --expected-descriptor-sha256)
      require_value "$@"
      EXPECTED_DESCRIPTOR_SHA256="$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')"
      shift 2
      ;;
    --verify-only) VERIFY_ONLY=true; shift ;;
    --allow-development-release) ALLOW_DEVELOPMENT_RELEASE=true; shift ;;
    --) shift; INSTALLER_ARGUMENTS=("$@"); break ;;
    -h|--help) usage; exit 0 ;;
    *) fail "unknown bootstrap option: $1 (use -- before managed installer options)" ;;
  esac
done

[[ "$CHANNEL" =~ ^[a-z][a-z0-9-]{0,31}$ ]] || fail "invalid release channel"
if [[ -n "$VERSION" ]]; then
  [[ "$VERSION" =~ ^[0-9A-Za-z][0-9A-Za-z.+-]{0,63}$ ]] || fail "invalid release version"
fi
BASE_URL="${BASE_URL%/}"
[[ "$BASE_URL" =~ ^https://[A-Za-z0-9.-]+(:[0-9]{1,5})?$ ]] || \
  fail "package base URL must be credential-free HTTPS without a path, query, or fragment"
[[ "$EXPECTED_FINGERPRINT" =~ ^[0-9a-f]{64}$ ]] || \
  fail "the public installer has no approved production release fingerprint"
if [[ -n "$EXPECTED_DESCRIPTOR_SHA256" ]]; then
  [[ "$EXPECTED_DESCRIPTOR_SHA256" =~ ^[0-9a-f]{64}$ ]] || \
    fail "expected release descriptor checksum is invalid"
fi
if [[ -n "$TRUSTED_RELEASE_KEY" ]]; then
  [[ "$TRUSTED_RELEASE_KEY" == /* && -f "$TRUSTED_RELEASE_KEY" && \
    ! -L "$TRUSTED_RELEASE_KEY" ]] || fail "trusted release key is missing or unsafe"
fi
if [[ "$ALLOW_DEVELOPMENT_RELEASE" == true && -z "$TRUSTED_RELEASE_KEY" ]]; then
  fail "development releases require an explicitly supplied external trust key"
fi
for command_name in awk curl openssl python3 sha256sum stat tar; do
  command -v "$command_name" >/dev/null 2>&1 || \
    fail "required command not found: $command_name"
done

case "$(uname -m)" in
  x86_64) ARCHITECTURE="amd64" ;;
  aarch64|arm64) ARCHITECTURE="arm64" ;;
  *) fail "unsupported architecture: $(uname -m)" ;;
esac

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/tranzify-watch-install.XXXXXX")"
cleanup() {
  rm -rf -- "$WORK_DIR"
}
trap cleanup EXIT

progress release_metadata

download() {
  local url="$1" output="$2" maximum_size="${3:-1048576}"
  curl \
    --fail \
    --silent \
    --show-error \
    --proto '=https' \
    --tlsv1.2 \
    --connect-timeout 15 \
    --max-time 600 \
    --max-filesize "$maximum_size" \
    --max-redirs 0 \
    --output "$output" \
    "$url"
}

PUBLIC_KEY="$WORK_DIR/tranzify-watch-release.pub"
if [[ -n "$TRUSTED_RELEASE_KEY" ]]; then
  install -m 0600 "$TRUSTED_RELEASE_KEY" "$PUBLIC_KEY"
else
  download "$BASE_URL/keys/release-v1.pub" "$PUBLIC_KEY"
  chmod 0600 "$PUBLIC_KEY"
fi
ACTUAL_FINGERPRINT="$(
  openssl pkey -pubin -in "$PUBLIC_KEY" -outform DER |
    openssl dgst -sha256 -r |
    awk '{ print $1 }'
)"
ACTUAL_FINGERPRINT="$(printf '%s' "$ACTUAL_FINGERPRINT" | tr '[:upper:]' '[:lower:]')"
[[ "$ACTUAL_FINGERPRINT" == "$EXPECTED_FINGERPRINT" ]] || \
  fail "downloaded release key does not match the installer trust anchor"

if [[ -n "$VERSION" ]]; then
  RELEASE_BASE="$BASE_URL/releases/v1/$VERSION/linux/$ARCHITECTURE"
else
  RELEASE_BASE="$BASE_URL/channels/$CHANNEL/linux/$ARCHITECTURE"
fi
download "$RELEASE_BASE/release.json" "$WORK_DIR/release.json"
download "$RELEASE_BASE/release.json.sig" "$WORK_DIR/release.json.sig"
download "$RELEASE_BASE/RELEASE-SIGNING-FINGERPRINT" \
  "$WORK_DIR/RELEASE-SIGNING-FINGERPRINT"

if [[ -n "$EXPECTED_DESCRIPTOR_SHA256" ]]; then
  ACTUAL_DESCRIPTOR_SHA256="$(sha256sum "$WORK_DIR/release.json" | awk '{ print $1 }')"
  [[ "$ACTUAL_DESCRIPTOR_SHA256" == "$EXPECTED_DESCRIPTOR_SHA256" ]] || \
    fail "signed release descriptor changed after approval"
fi

RECORDED_FINGERPRINT="$(
  tr -d '[:space:]' < "$WORK_DIR/RELEASE-SIGNING-FINGERPRINT" |
    tr '[:upper:]' '[:lower:]'
)"
[[ "$RECORDED_FINGERPRINT" == "$EXPECTED_FINGERPRINT" ]] || \
  fail "release channel fingerprint does not match the installer trust anchor"
openssl dgst -sha256 \
  -verify "$PUBLIC_KEY" \
  -signature "$WORK_DIR/release.json.sig" \
  "$WORK_DIR/release.json" >/dev/null || fail "release descriptor signature is invalid"

VALUES_FILE="$WORK_DIR/release-values"
python3 - \
  "$WORK_DIR/release.json" \
  "$EXPECTED_FINGERPRINT" \
  "$ARCHITECTURE" \
  "$ALLOW_DEVELOPMENT_RELEASE" \
  "$VALUES_FILE" <<'PY'
import json
import re
import sys

release_path, fingerprint, host_arch, allow_development, output_path = sys.argv[1:]
with open(release_path, "r", encoding="utf-8") as release_file:
    release = json.load(release_file)

version = release.get("version")
target = release.get("target") or {}
artifact = release.get("artifact") or {}
root = release.get("bundle_root")
integrity = release.get("integrity") or {}
distribution = release.get("distribution") or {}
if release.get("schema_version") != "1.1.0" or release.get("product") != "Tranzify Watch":
    raise SystemExit("unsupported release descriptor")
if not isinstance(version, str) or not re.fullmatch(r"[0-9A-Za-z][0-9A-Za-z.+-]{0,63}", version):
    raise SystemExit("invalid release version")
if target != {"os": "linux", "arch": host_arch}:
    raise SystemExit("release target does not match this host")
profile = distribution.get("profile")
if profile not in {"development", "production"}:
    raise SystemExit("invalid release distribution profile")
if profile != "production" and allow_development != "true":
    raise SystemExit("public bootstrap refuses a non-production release")
if (
    distribution.get("method") != "direct"
    or not isinstance(distribution.get("base_url"), str)
    or not re.fullmatch(r"https://[^/?#]+", distribution["base_url"])
):
    raise SystemExit("invalid direct-release distribution")
if profile == "production" and distribution["base_url"] != "https://packages.tranzify.watch":
    raise SystemExit("production release points to an unapproved origin")
expected_root = f"tranzify-watch-{version}-linux-{host_arch}"
expected_file = f"{expected_root}.tar.gz"
expected_path = f"releases/v1/{version}/linux/{host_arch}/{expected_file}"
if root != expected_root or artifact.get("file") != expected_file or artifact.get("path") != expected_path:
    raise SystemExit("release artifact path is not canonical")
if artifact.get("media_type") != "application/gzip":
    raise SystemExit("unsupported release media type")
if not re.fullmatch(r"[0-9a-f]{64}", str(artifact.get("sha256", ""))):
    raise SystemExit("invalid release archive checksum")
size = artifact.get("size_bytes")
if (
    not isinstance(size, int)
    or isinstance(size, bool)
    or size <= 0
    or size > 2 * 1024 * 1024 * 1024
):
    raise SystemExit("invalid release archive size")
if (
    integrity.get("descriptor_signature") != "RSA-SHA256"
    or integrity.get("bundle_manifest") != "SHA256SUMS"
    or "bundle_signature" in integrity
    or integrity.get("release_key_fingerprint") != fingerprint
):
    raise SystemExit("invalid release integrity metadata")
with open(output_path, "w", encoding="utf-8") as output_file:
    output_file.write(f"{version}\n{root}\n{expected_file}\n{expected_path}\n{artifact['sha256']}\n{size}\n")
PY

RELEASE_VERSION="$(sed -n '1p' "$VALUES_FILE")"
BUNDLE_ROOT="$(sed -n '2p' "$VALUES_FILE")"
ARCHIVE_FILE="$(sed -n '3p' "$VALUES_FILE")"
ARTIFACT_PATH="$(sed -n '4p' "$VALUES_FILE")"
EXPECTED_SHA256="$(sed -n '5p' "$VALUES_FILE")"
EXPECTED_SIZE="$(sed -n '6p' "$VALUES_FILE")"
if [[ -n "$VERSION" && "$RELEASE_VERSION" != "$VERSION" ]]; then
  fail "signed release version does not match the requested version"
fi

ARCHIVE_PATH="$WORK_DIR/$ARCHIVE_FILE"
progress release_download
download "$BASE_URL/$ARTIFACT_PATH" "$ARCHIVE_PATH" "2147483648"
[[ "$(stat -c '%s' "$ARCHIVE_PATH")" == "$EXPECTED_SIZE" ]] || \
  fail "release archive size mismatch"
[[ "$(sha256sum "$ARCHIVE_PATH" | awk '{ print $1 }')" == "$EXPECTED_SHA256" ]] || \
  fail "release archive checksum mismatch"

progress release_verify
python3 - "$ARCHIVE_PATH" "$BUNDLE_ROOT" <<'PY'
import pathlib
import sys
import tarfile

archive_path, bundle_root = sys.argv[1:]
seen = set()
total_size = 0
with tarfile.open(archive_path, mode="r:gz") as archive:
    members = archive.getmembers()
    if not members or len(members) > 100_000:
        raise SystemExit("release archive has an invalid member count")
    for member in members:
        name = member.name
        path = pathlib.PurePosixPath(name)
        if (
            not name
            or name.startswith("/")
            or "\\" in name
            or "\r" in name
            or "\n" in name
            or "//" in name
            or name.startswith("./")
            or "/./" in name
            or name.endswith("/.")
            or name.endswith("/..")
            or "." in path.parts
            or ".." in path.parts
            or not path.parts
            or path.parts[0] != bundle_root
            or name in seen
        ):
            raise SystemExit(f"release archive contains an unsafe member: {name!r}")
        if not (member.isfile() or member.isdir()):
            raise SystemExit(f"release archive contains a link or special file: {name!r}")
        seen.add(name)
        total_size += member.size
        if total_size > 4 * 1024 * 1024 * 1024:
            raise SystemExit("release archive expands beyond the 4 GiB safety limit")
PY

tar \
  --extract \
  --gzip \
  --file "$ARCHIVE_PATH" \
  --directory "$WORK_DIR" \
  --no-same-owner \
  --no-same-permissions
BUNDLE_DIR="$WORK_DIR/$BUNDLE_ROOT"
[[ -d "$BUNDLE_DIR" && ! -L "$BUNDLE_DIR" ]] || fail "release bundle root is missing or unsafe"
[[ -x "$BUNDLE_DIR/bin/verify-release-bundle" ]] || fail "bundle verifier is missing"
"$BUNDLE_DIR/bin/verify-release-bundle" \
  --bundle-dir "$BUNDLE_DIR"

if [[ "$VERIFY_ONLY" == true ]]; then
  printf 'Tranzify Watch release %s for linux/%s is trusted and complete.\n' \
    "$RELEASE_VERSION" "$ARCHITECTURE"
  exit 0
fi
[[ "${EUID:-$(id -u)}" -eq 0 ]] || fail "run the bootstrap installer as root (sudo)"
"$BUNDLE_DIR/install.sh" \
  --bundle-dir "$BUNDLE_DIR" \
  "${INSTALLER_ARGUMENTS[@]}"
