#!/usr/bin/env bash

set -euo pipefail
umask 077
export LC_ALL=C
export TZ=UTC
unset GZIP TAR_OPTIONS

readonly public_base=https://get.kinra.ai/paddock
readonly manifest_url=$public_base/version.json

work_dir=

cleanup() {
  if [[ -n ${work_dir:-} && -d $work_dir && ! -L $work_dir ]]; then
    chmod -R u+rwX -- "$work_dir" 2>/dev/null || true
    rm -rf -- "$work_dir"
  fi
}

die() {
  printf 'error: %s\n' "$*" >&2
  exit 1
}

usage() {
  printf 'Usage: %s [--check]\n' "${0##*/}"
  printf '\n'
  printf 'Downloads and verifies the current Paddock Linux x86-64 archive,\n'
  printf 'then runs its packaged system installer. --check changes nothing.\n'
}

main() {
mode=install
if [[ $# -gt 1 ]]; then
  usage >&2
  exit 2
fi
if [[ $# -eq 1 ]]; then
  case $1 in
    --check) mode=check ;;
    --help)
      usage
      exit 0
      ;;
    *)
      usage >&2
      exit 2
      ;;
  esac
fi

required_commands=(awk chmod curl find grep head mkdir mktemp rm sha256sum stat tail tar uname wc)
if [[ $mode == install && $EUID -ne 0 ]]; then
  required_commands+=(sudo)
fi
for command_name in "${required_commands[@]}"; do
  if ! command -v "$command_name" >/dev/null 2>&1; then
    die "required command not found: $command_name"
  fi
done

if [[ $(uname -s) != Linux ]]; then
  die "Paddock release archives support Linux only"
fi
if [[ $(uname -m) != x86_64 ]]; then
  die "Paddock alpha supports Linux x86-64 only; unsupported architecture: $(uname -m)"
fi

tar_version=$(tar --version 2>/dev/null || true)
if [[ $tar_version != *"GNU tar"* ]]; then
  die "archive validation requires GNU tar"
fi

tmp_parent=${TMPDIR:-/tmp}
if [[ $tmp_parent != /* || ! -d $tmp_parent || -L $tmp_parent || ! -w $tmp_parent ]]; then
  die "TMPDIR must name a writable, non-symlinked absolute directory"
fi
work_dir=$(mktemp -d "$tmp_parent/paddock-install.XXXXXXXX")
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
chmod 0700 -- "$work_dir"
if [[ $(stat -c '%a' "$work_dir") != 700 ]]; then
  die "temporary directory is not owner-only"
fi

manifest_file=$work_dir/version.json
printf 'Fetching Paddock release manifest...\n'
if ! curl \
  --disable \
  --fail \
  --silent \
  --show-error \
  --location \
  --proto '=https' \
  --proto-redir '=https' \
  --tlsv1.2 \
  --output "$manifest_file" \
  "$manifest_url"; then
  die "unable to fetch $manifest_url"
fi
if [[ ! -f $manifest_file || -L $manifest_file ]]; then
  die "release manifest download is missing or unsafe"
fi
manifest_size=$(wc -c <"$manifest_file")
if [[ ! $manifest_size =~ ^[0-9]+$ || $manifest_size -lt 1 || $manifest_size -gt 8192 ]]; then
  die "release manifest has an invalid size"
fi
if [[ $(head -n 1 "$manifest_file") != "{" || $(tail -n 1 "$manifest_file") != "}" ]]; then
  die "release manifest does not match the required schema"
fi

line_count() {
  grep -Fxc -- "$1" "$manifest_file" || true
}
require_line() {
  if [[ $(line_count "$1") -ne 1 ]]; then
    die "release manifest does not match the required schema"
  fi
}
top_string() {
  local key=$1
  awk -F'"' -v key="$key" '
    $1 == "  " && $2 == key && $3 == ": " && ($5 == "," || $5 == "") {
      count += 1
      value = $4
    }
    END { if (count == 1) print value; else exit 1 }
  ' "$manifest_file"
}
object_string() {
  local object=$1
  local key=$2
  awk -F'"' -v object="$object" -v key="$key" '
    $0 == "    \"" object "\": {" { inside = 1; next }
    inside && $1 == "      " && $2 == key && $3 == ": " &&
      ($5 == "," || $5 == "") {
      count += 1
      value = $4
    }
    inside && ($0 == "    }," || $0 == "    }") { inside = 0 }
    END { if (count == 1) print value; else exit 1 }
  ' "$manifest_file"
}
platform_string() {
  local key=$1
  awk -F'"' -v key="$key" '
    $0 == "  \"platform\": {" { inside = 1; next }
    inside && $1 == "    " && $2 == key && $3 == ": " &&
      ($5 == "," || $5 == "") {
      count += 1
      value = $4
    }
    inside && ($0 == "  }," || $0 == "  }") { inside = 0 }
    END { if (count == 1) print value; else exit 1 }
  ' "$manifest_file"
}

require_line '  "schema_version": 1,'
require_line '  "product": "paddock",'
require_line '  "channel": "alpha",'
require_line '  "platform": {'
require_line '  "artifacts": {'
for artifact_name in archive checksums sbom metadata compatibility; do
  require_line "    \"$artifact_name\": {"
done

version=$(top_string version) ||
  die "release manifest version is malformed"
source_commit=$(top_string source_commit) ||
  die "release manifest source commit is malformed"
published_at=$(top_string published_at) ||
  die "release manifest publish time is malformed"
platform_os=$(platform_string os) ||
  die "release manifest platform OS is malformed"
platform_arch=$(platform_string arch) ||
  die "release manifest platform architecture is malformed"

declare -A artifact_paths=()
declare -A artifact_hashes=()
for artifact_name in archive checksums sbom metadata compatibility; do
  artifact_paths[$artifact_name]=$(object_string "$artifact_name" path) ||
    die "release manifest $artifact_name path is malformed"
  artifact_hashes[$artifact_name]=$(object_string "$artifact_name" sha256) ||
    die "release manifest $artifact_name SHA-256 is malformed"
done

semver_pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
if [[ ! $version =~ $semver_pattern ]]; then
  die "release manifest contains an invalid semantic version"
fi
if [[ ! $source_commit =~ ^[0-9a-f]{40}$ ]]; then
  die "release manifest contains an invalid source commit"
fi
if [[ ! $published_at =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]]; then
  die "release manifest contains an invalid publish time"
fi
if [[ $platform_os != linux || $platform_arch != amd64 ]]; then
  die "release manifest contains an unsupported platform"
fi

artifact_base=paddock_${version}_linux_amd64
declare -A expected_paths=(
  [archive]="releases/$version/$artifact_base.tar.gz"
  [checksums]="releases/$version/checksums.txt"
  [sbom]="releases/$version/$artifact_base.spdx.json"
  [metadata]="releases/$version/$artifact_base.metadata.json"
  [compatibility]="releases/$version/$artifact_base.compatibility.json"
)
for artifact_name in archive checksums sbom metadata compatibility; do
  if [[ ${artifact_paths[$artifact_name]} != "${expected_paths[$artifact_name]}" ]]; then
    die "release manifest contains a non-canonical $artifact_name path"
  fi
  if [[ ! ${artifact_hashes[$artifact_name]} =~ ^[0-9a-f]{64}$ ]]; then
    die "release manifest contains an invalid $artifact_name SHA-256"
  fi
done

archive_file=$work_dir/$artifact_base.tar.gz
archive_url=$public_base/${artifact_paths[archive]}
printf 'Downloading Paddock %s for linux/amd64...\n' "$version"
if ! curl \
  --disable \
  --fail \
  --silent \
  --show-error \
  --location \
  --proto '=https' \
  --proto-redir '=https' \
  --tlsv1.2 \
  --output "$archive_file" \
  "$archive_url"; then
  die "unable to fetch immutable release archive"
fi
if [[ ! -f $archive_file || -L $archive_file ]]; then
  die "release archive download is missing or unsafe"
fi
checksum_output=$(sha256sum --binary "$archive_file") ||
  die "unable to compute release archive checksum"
actual_sha256=${checksum_output%% *}
if [[ ! $actual_sha256 =~ ^[0-9a-f]{64}$ ||
  $actual_sha256 != "${artifact_hashes[archive]}" ]]; then
  die "release archive SHA-256 mismatch"
fi

archive_listing=$work_dir/archive.list
archive_verbose_listing=$work_dir/archive.verbose.list
if ! tar \
  --list \
  --gzip \
  --file "$archive_file" \
  --quoting-style=escape >"$archive_listing"; then
  die "release archive cannot be listed"
fi
if ! tar \
  --list \
  --verbose \
  --gzip \
  --file "$archive_file" \
  --quoting-style=escape >"$archive_verbose_listing"; then
  die "release archive types cannot be inspected"
fi

declare -A seen_entries=()
entry_count=0
installer_seen=0
while IFS= read -r entry || [[ -n $entry ]]; do
  entry_count=$((entry_count + 1))
  if [[ -z $entry || ! $entry =~ ^[A-Za-z0-9._/+:-]+$ ]]; then
    die "release archive contains a malformed path"
  fi
  case $entry in
    "$artifact_base"|"$artifact_base/"*) ;;
    *) die "release archive entry escapes its versioned root" ;;
  esac
  case $entry in
    */../*|*/..|*/./*|*/.|*//*)
      die "release archive contains an unsafe path component"
      ;;
  esac
  if [[ -n ${seen_entries[$entry]+present} ]]; then
    die "release archive contains a duplicate path"
  fi
  seen_entries[$entry]=1
  if [[ $entry == "$artifact_base/install.sh" ]]; then
    installer_seen=1
  fi
done <"$archive_listing"
if [[ $entry_count -lt 1 || $installer_seen -ne 1 ]]; then
  die "release archive is missing its packaged installer"
fi

while IFS= read -r verbose_entry || [[ -n $verbose_entry ]]; do
  case ${verbose_entry:0:1} in
    -|d) ;;
    *) die "release archive contains a link or special file" ;;
  esac
done <"$archive_verbose_listing"

extract_dir=$work_dir/extract
mkdir -m 0700 -- "$extract_dir"
if ! tar \
  --extract \
  --gzip \
  --file "$archive_file" \
  --directory "$extract_dir" \
  --no-same-owner \
  --no-same-permissions \
  --delay-directory-restore; then
  die "release archive extraction failed"
fi
if [[ -n $(find "$extract_dir" -type l -print -quit) ]]; then
  die "release archive extracted a symbolic link"
fi
payload_root=$extract_dir/$artifact_base
packaged_installer=$payload_root/install.sh
if [[ ! -d $payload_root || -L $payload_root ||
  ! -f $packaged_installer || -L $packaged_installer ||
  ! -x $packaged_installer ]]; then
  die "release archive did not produce a safe executable installer"
fi

printf 'Verified Paddock %s (%s).\n' "$version" "$actual_sha256"
if [[ $mode == check ]]; then
  printf 'Check complete; no installation changes were made.\n'
  exit 0
fi

printf 'Running the packaged system installer...\n'
if [[ $EUID -eq 0 ]]; then
  "$packaged_installer"
else
  sudo -- "$packaged_installer"
fi
}

# Keep every mutating operation inside main. When this file is supplied through
# process substitution, Bash must parse the complete function and final call
# before it can download an archive or invoke the packaged installer.
main "$@"
