#!/usr/bin/env bash

set -u

usage() {
  printf '%s\n' \
    "usage: readiness-probe --dependency PATH --capacity-path PATH --min-free-kb N" \
    "       --backup-file PATH --now EPOCH --max-backup-age SECONDS" \
    "       --monitor-file PATH --access-file PATH --rollback-file PATH" >&2
}

DEPENDENCY_PATH=
CAPACITY_PATH=
MIN_FREE_KB=
BACKUP_FILE=
NOW_EPOCH=
MAX_BACKUP_AGE=
MONITOR_FILE=
ACCESS_FILE=
ROLLBACK_FILE=

while (( $# > 0 )); do
  case "$1" in
    --dependency)     key=DEPENDENCY_PATH ;;
    --capacity-path)  key=CAPACITY_PATH ;;
    --min-free-kb)    key=MIN_FREE_KB ;;
    --backup-file)    key=BACKUP_FILE ;;
    --now)            key=NOW_EPOCH ;;
    --max-backup-age) key=MAX_BACKUP_AGE ;;
    --monitor-file)   key=MONITOR_FILE ;;
    --access-file)    key=ACCESS_FILE ;;
    --rollback-file)  key=ROLLBACK_FILE ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      printf 'readiness-probe: unknown option: %s\n' "$1" >&2
      usage
      exit 2
      ;;
  esac

  if (( $# < 2 )); then
    printf 'readiness-probe: option requires a value: %s\n' "$1" >&2
    usage
    exit 2
  fi
  printf -v "$key" '%s' "$2"
  shift 2
done

for required in \
  DEPENDENCY_PATH CAPACITY_PATH MIN_FREE_KB BACKUP_FILE NOW_EPOCH \
  MAX_BACKUP_AGE MONITOR_FILE ACCESS_FILE ROLLBACK_FILE
do
  if [[ -z "${!required}" ]]; then
    printf 'readiness-probe: missing required options\n' >&2
    usage
    exit 2
  fi
done

for numeric in MIN_FREE_KB NOW_EPOCH MAX_BACKUP_AGE; do
  if [[ ! ${!numeric} =~ ^[0-9]+$ ]]; then
    printf 'readiness-probe: %s must be a non-negative integer\n' "$numeric" >&2
    exit 2
  fi
done

failures=0

fail() {
  printf '%s\n' "$1"
  failures=$((failures + 1))
}

check_dependency() {
  if [[ ! -f "$DEPENDENCY_PATH" || ! -x "$DEPENDENCY_PATH" ]]; then
    fail "FAIL dependency: not an executable file: $DEPENDENCY_PATH"
  fi
}

check_capacity() {
  local available

  if [[ ! -d "$CAPACITY_PATH" ]]; then
    fail "FAIL capacity: path is not a directory: $CAPACITY_PATH"
    return
  fi

  available=$(LC_ALL=C df -Pk -- "$CAPACITY_PATH" 2>/dev/null | awk 'NR == 2 { print $4 }')
  if [[ ! $available =~ ^[0-9]+$ ]]; then
    fail "FAIL capacity: could not determine available space: $CAPACITY_PATH"
  elif (( available < MIN_FREE_KB )); then
    fail "FAIL capacity: ${available} KB available; requires ${MIN_FREE_KB} KB"
  fi
}

check_backup() {
  local backup_epoch age

  if [[ ! -s "$BACKUP_FILE" ]]; then
    fail "FAIL backup: missing or empty artifact: $BACKUP_FILE"
    return
  fi

  backup_epoch=$(stat -c '%Y' -- "$BACKUP_FILE" 2>/dev/null) || {
    fail "FAIL backup: could not read timestamp: $BACKUP_FILE"
    return
  }

  if [[ ! $backup_epoch =~ ^[0-9]+$ ]]; then
    fail "FAIL backup: invalid timestamp: $BACKUP_FILE"
    return
  fi

  age=$((NOW_EPOCH - backup_epoch))
  if (( age < 0 )); then
    fail "FAIL backup: timestamp is in the future: $BACKUP_FILE"
  elif (( age > MAX_BACKUP_AGE )); then
    fail "FAIL backup: artifact is ${age} seconds old; maximum is ${MAX_BACKUP_AGE}"
  fi
}

read_status() {
  local path=$1
  local value=

  IFS= read -r value < "$path" || true
  printf '%s' "$value"
}

check_monitoring() {
  local status

  if [[ ! -s "$MONITOR_FILE" ]]; then
    fail "FAIL monitoring: missing or empty artifact: $MONITOR_FILE"
    return
  fi

  status=$(read_status "$MONITOR_FILE")
  if [[ $status != healthy ]]; then
    fail "FAIL monitoring: status is '$status'; expected 'healthy'"
  fi
}

check_access() {
  local status

  if [[ ! -s "$ACCESS_FILE" ]]; then
    fail "FAIL access: missing or empty artifact: $ACCESS_FILE"
    return
  fi

  status=$(read_status "$ACCESS_FILE")
  if [[ $status != granted ]]; then
    fail "FAIL access: status is '$status'; expected 'granted'"
  fi
}

check_rollback() {
  # Rollback must be an independently staged, non-empty local artifact.
  if [[ ! -s "$BACKUP_FILE" ]]; then
    fail "FAIL rollback: missing or empty artifact: $ROLLBACK_FILE"
  fi
}

check_dependency
check_capacity
check_backup
check_monitoring
check_access
check_rollback

if (( failures > 0 )); then
  exit 1
fi

printf 'READY\n'
