#!/usr/bin/env bash
#
# Invalidate SwiftBuild products when the compiler/Xcode or package graph changes.
#
# SwiftBuild's explicit Clang module cache lives under <scratch>/out. SwiftPM does
# not reliably invalidate every dependency-scan record when a DEVELOPMENT-SNAPSHOT
# toolchain, Xcode beta, or package manifest changes in place. The stale records then
# make downstream Swift targets fail to resolve generated C modules such as
# _AtomicsShims and CNIO*.
#
# Keep the expensive, toolchain-independent SwiftPM state (repositories, checkouts,
# and downloaded binary artifacts) and discard only generated build products.

set -euo pipefail

root="${1:-$PWD}"
reset_mode="${2:-}"

# There is nothing useful to fingerprint in non-Swift build environments. This also
# keeps light Makefile inspection in the Linux control container side-effect free.
command -v swift >/dev/null 2>&1 || exit 0

script_dir="$(cd "$(dirname "$0")" && pwd)"
scratch_args="$(bash "$script_dir/build-scratch.sh" "$root")"
if [[ "$scratch_args" == "--scratch-path "* ]]; then
  scratch="${scratch_args#--scratch-path }"
else
  scratch="$root/.build"
fi

swift_path="$(command -v swift)"
swiftc_path="$(command -v swiftc 2>/dev/null || true)"

file_fingerprint() {
  local path="$1"
  if [[ ! -f "$path" ]]; then
    printf 'missing'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$path" | cut -d ' ' -f 1
  elif command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$path" | cut -d ' ' -f 1
  else
    cksum "$path" | awk '{ print $1 ":" $2 }'
  fi
}

identity="$({
  printf 'swift=%s\n' "$swift_path"
  printf 'swiftc=%s\n' "$swiftc_path"
  "$swift_path" --version 2>&1
  printf 'DEVELOPER_DIR=%s\n' "${DEVELOPER_DIR:-}"
  printf 'SDKROOT=%s\n' "${SDKROOT:-}"
  printf 'TOOLCHAINS=%s\n' "${TOOLCHAINS:-}"
  if command -v xcodebuild >/dev/null 2>&1; then
    xcodebuild -version 2>&1 || true
  fi
  if command -v xcrun >/dev/null 2>&1; then
    printf 'macos-sdk=%s\n' "$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || true)"
    printf 'clang=%s\n' "$(xcrun --find clang 2>/dev/null || true)"
  fi
  # SwiftBuild has also retained invalid explicit-module scan records after a
  # Package.swift graph change with an otherwise unchanged toolchain. Hash the root
  # manifest, resolution, and the one local package in this repository.
  for package_file in \
    "$root/Package.swift" \
    "$root/Package.resolved" \
    "$root/third_party/containerization/Package.swift"
  do
    printf 'package-graph:%s=%s\n' \
      "${package_file#"$root"/}" "$(file_fingerprint "$package_file")"
  done
} )"

mkdir -p "$scratch"
stamp="$scratch/.nucleic-swift-toolchain"
previous=""
if [[ -f "$stamp" ]]; then
  previous="$(<"$stamp")"
fi

# An absent stamp means this checkout predates the guard. Adopt the current identity
# without forcing a one-time full rebuild; subsequent identity changes are detected.
if [[ "$reset_mode" == "--reset-products" && -d "$scratch/out" ]]; then
  echo "▸ Resetting invalid SwiftBuild products in $scratch/out" >&2
  rm -rf "$scratch/out"
elif [[ -n "$previous" && "$previous" != "$identity" && -d "$scratch/out" ]]; then
  echo "▸ Swift/Xcode or package graph changed; clearing stale SwiftBuild products in $scratch/out" >&2
  rm -rf "$scratch/out"
fi

tmp_stamp="$stamp.tmp.$$"
printf '%s\n' "$identity" > "$tmp_stamp"
mv -f "$tmp_stamp" "$stamp"
