#!/bin/sh
# Fake kubectl for hack/migration-55-fluxcd-orphan.bats. It serves only the
# calls migration 55 (and lib/cozystack-version.sh) make; behaviour is driven by
# FAKE_* env and every invocation is appended to $FAKE_CMDLOG so the test can
# assert on which HelmReleases were suspended, finalizer-patched and deleted, in
# what order, and which were never touched.
#
# Migration 55 fully-qualifies the resource as
# `helmreleases.helm.toolkit.fluxcd.io` on every call, so this fake matches (and
# reads the object name after) that exact token. The verify read and the fleet
# scan therefore share a prefix; they are told apart by the verify read's
# `-o jsonpath=` selector, matched first below.
#
#   FAKE_CMDLOG          file to append a one-line record of each call to
#   FAKE_HRS             newline list of "<ns> <name> <chartRef.name>" — one per
#                        HelmRelease the -A fleet scan should report. jq in the
#                        migration selects the two fluxcd-addon chartRef names;
#                        rows with any other chartRef must be listed but ignored.
#   FAKE_LIST_FAIL       non-empty => the -A fleet scan exits 1 with this stderr
#   FAKE_PATCH_FAIL      non-empty => every `patch` exits 1 with this stderr
#   FAKE_DELETE_FAIL     non-empty => every `delete` exits 1 with this stderr
#   FAKE_FINALIZER_STICKS non-empty => the verify read returns a still-present
#                        finalizer, modelling a patch the apiserver did not honour
#   FAKE_VERIFY_READ_FAIL non-empty => the finalizer verify read itself exits 1
#                        with this stderr, modelling an API error on the read
#                        that gates the delete — the suspend and finalizer
#                        patches before it still succeed
#
# The failure knobs exist because the point of migration 55 is fail-closed: a
# kubectl error, or a finalizer that survives the patch, must ABORT the migration
# before it deletes a still-finalized object or stamps the version. A fake that
# can only succeed cannot test that.
set -u

log() { [ -n "${FAKE_CMDLOG:-}" ] && printf '%s\n' "$*" >> "$FAKE_CMDLOG"; }
log "KUBECTL $*"

args="$*"

# arg_after KEY -- echoes the token following KEY in the argument vector.
arg_after() { k="$1"; shift; p=""; for a in "$@"; do [ "$p" = "$k" ] && { printf '%s' "$a"; return; }; p="$a"; done; }

case "$args" in
  # Version stamp: kubectl apply --filename -
  *"apply --filename -"*)
    manifest=$(cat)
    # Record the version actually stamped. A migration that stamped the wrong
    # number would loop the runner forever, and asserting a bare "STAMP" would
    # not notice.
    v=$(printf '%s\n' "$manifest" | sed -n 's/^  version: "\{0,1\}\([0-9]\{1,\}\)"\{0,1\}$/\1/p' | head -1)
    log "STAMP ${v:-unknown}"
    exit 0
    ;;

  # Per-namespace finalizer verify read, gating the delete. Shares the fleet
  # scan's resource token, so it is matched FIRST and distinguished by the
  # `-o jsonpath=` selector; --ignore-not-found (the migration's own flag) is
  # just another token here and needs no handling. Empty output => the finalizer
  # is gone and the delete may proceed; a non-empty value => it survived the
  # patch and the migration must refuse to delete.
  *"get helmreleases.helm.toolkit.fluxcd.io"*"jsonpath"*)
    if [ -n "${FAKE_VERIFY_READ_FAIL:-}" ]; then
      printf '%s\n' "$FAKE_VERIFY_READ_FAIL" >&2
      exit 1
    fi
    if [ -n "${FAKE_FINALIZER_STICKS:-}" ]; then
      printf '%s' '["helm.toolkit.fluxcd.io/finalizer"]'
    fi
    exit 0
    ;;

  # Fleet scan: every HelmRelease across all namespaces, as JSON. The migration
  # pipes this through jq to pick out the two fluxcd-addon chartRef names, so the
  # selection logic under test is jq's, not the fake's — the fake just emits what
  # FAKE_HRS describes, matching and non-matching rows alike.
  *"get helmreleases.helm.toolkit.fluxcd.io"*)
    if [ -n "${FAKE_LIST_FAIL:-}" ]; then
      printf '%s\n' "$FAKE_LIST_FAIL" >&2
      exit 1
    fi
    printf '{"items":['
    printf '%s\n' "${FAKE_HRS:-}" | {
      sep=""
      while read -r ns name chart; do
        [ -n "$ns" ] || continue
        printf '%s{"metadata":{"namespace":"%s","name":"%s"},"spec":{"chartRef":{"name":"%s"}}}' \
          "$sep" "$ns" "$name" "$chart"
        sep=","
      done
    }
    printf ']}\n'
    exit 0
    ;;

  # Suspend and finalizer-drop patches.
  *"patch helmreleases.helm.toolkit.fluxcd.io"*)
    if [ -n "${FAKE_PATCH_FAIL:-}" ]; then
      printf '%s\n' "$FAKE_PATCH_FAIL" >&2
      exit 1
    fi
    ns=$(arg_after -n "$@")
    name=$(arg_after helmreleases.helm.toolkit.fluxcd.io "$@")
    case "$args" in
      *suspend*)    log "SUSPEND $ns $name" ;;
      *finalizers*) log "FINALIZER-PATCH $ns $name" ;;
    esac
    exit 0
    ;;

  # The orphaning delete itself.
  *"delete helmreleases.helm.toolkit.fluxcd.io"*)
    if [ -n "${FAKE_DELETE_FAIL:-}" ]; then
      printf '%s\n' "$FAKE_DELETE_FAIL" >&2
      exit 1
    fi
    ns=$(arg_after -n "$@")
    name=$(arg_after helmreleases.helm.toolkit.fluxcd.io "$@")
    log "DELETE $ns $name"
    exit 0
    ;;
esac

# Any call the migration makes that is not modelled above is a drift between the
# script and this fake — fail loudly rather than let the migration proceed
# against a call the test never actually reasoned about.
log "UNHANDLED $args"
printf 'fake kubectl: unmodelled call: %s\n' "$args" >&2
exit 1
