#!/usr/bin/env bash
# Sync the repo dispatch chassis tools into the daemon bin directory.
#
# This script intentionally does not call launchctl or restart any daemon. It
# only copies files, or reports copy drift in --check mode.
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEST_DIR="${DISPATCH_CHASSIS_BIN_DIR:-$HOME/.recoil/bin}"
MODE="sync"
DRY_RUN=0

TOOLS=(
  dispatch_reaper.sh
  dispatch_status.py
  dispatch_notify.sh
  dispatch_heartbeat_watch.sh
  audit_dispatch.py
)

usage() {
  cat >&2 <<'USAGE'
usage:
  deploy_dispatch_chassis.sh [--dry-run]
  deploy_dispatch_chassis.sh --check [--dry-run]

Syncs dispatch chassis tools from this repo's recoil/pipeline/tools directory
to ~/.recoil/bin. --check reports per-file shasum drift and exits non-zero if
any deployed copy differs or is missing.
USAGE
}

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

sha_for() {
  shasum "$1" | awk '{print $1}'
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --check)
      MODE="check"
      ;;
    --dry-run)
      DRY_RUN=1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      usage
      die "unknown argument: $1"
      ;;
  esac
  shift
done

check_drift() {
  local drift=0
  local tool repo_file deployed_file repo_sha deployed_sha status

  for tool in "${TOOLS[@]}"; do
    repo_file="$SCRIPT_DIR/$tool"
    deployed_file="$DEST_DIR/$tool"

    [ -f "$repo_file" ] || die "repo tool missing: $repo_file"
    repo_sha="$(sha_for "$repo_file")"

    if [ -f "$deployed_file" ]; then
      deployed_sha="$(sha_for "$deployed_file")"
    else
      deployed_sha="MISSING"
    fi

    if [ "$repo_sha" = "$deployed_sha" ]; then
      status="OK"
    else
      status="DRIFT"
      drift=1
    fi

    printf '%s\n' "$tool"
    printf '  repo:     %s  %s\n' "$repo_sha" "$repo_file"
    printf '  deployed: %s  %s\n' "$deployed_sha" "$deployed_file"
    printf '  status:   %s\n' "$status"
  done

  return "$drift"
}

sync_tools() {
  local tool repo_file deployed_file repo_sha deployed_sha tmp

  if [ "$DRY_RUN" -eq 1 ]; then
    printf 'DRY_RUN: would ensure directory %s\n' "$DEST_DIR"
  else
    mkdir -p "$DEST_DIR" || die "failed to create $DEST_DIR"
  fi

  for tool in "${TOOLS[@]}"; do
    repo_file="$SCRIPT_DIR/$tool"
    deployed_file="$DEST_DIR/$tool"

    [ -f "$repo_file" ] || die "repo tool missing: $repo_file"
    repo_sha="$(sha_for "$repo_file")"

    if [ -f "$deployed_file" ]; then
      deployed_sha="$(sha_for "$deployed_file")"
    else
      deployed_sha="MISSING"
    fi

    if [ "$repo_sha" = "$deployed_sha" ]; then
      printf 'up to date: %s (%s)\n' "$tool" "$repo_sha"
      continue
    fi

    if [ "$DRY_RUN" -eq 1 ]; then
      printf 'DRY_RUN: would copy %s -> %s (repo %s, deployed %s)\n' \
        "$repo_file" "$deployed_file" "$repo_sha" "$deployed_sha"
      continue
    fi

    tmp="$deployed_file.tmp.$$"
    rm -f "$tmp"
    cp -p "$repo_file" "$tmp" || {
      rm -f "$tmp"
      die "failed to stage $tool"
    }
    mv "$tmp" "$deployed_file" || {
      rm -f "$tmp"
      die "failed to deploy $tool"
    }
    printf 'deployed: %s (%s)\n' "$tool" "$repo_sha"
  done
}

case "$MODE" in
  check)
    check_drift
    ;;
  sync)
    sync_tools
    ;;
  *)
    die "internal mode error: $MODE"
    ;;
esac
