#!/bin/sh
# Fake kubectl for the migration-56 unit test (hack/migration-56-adopt-values.bats).
# Mocks only the cluster boundary. Env:
#   FAKE_HR_LIST  path to the `get helmrelease -o json` list fixture
#   FAKE_CHILD_HR path to write the child HelmRelease that `apply -f -` receives
#   FAKE_CMDLOG   path to append a call log (APPLY-HR / STAMP / annotate / label)
#   FAKE_GET_FAIL substring: any `get` whose args match it fails with a
#                 transient server error (message overridable via
#                 FAKE_GET_FAIL_MSG, e.g. a conversion-webhook failure whose
#                 text happens to contain "not found"), so the migration's
#                 absence-vs-failure classification is exercised
#   FAKE_WARN_STDERR when set, every successful get prints a deprecation
#                 warning to stderr first, the way real kubectl does -- data
#                 captured with 2>&1 would be contaminated by it
# Absence honors --ignore-not-found the way real kubectl does: with the flag a
# missing object is SUCCESS with empty output; without it, "Error from server
# (NotFound)" on stderr and exit 1.
log() { [ -n "${FAKE_CMDLOG:-}" ] && echo "$*" >> "$FAKE_CMDLOG"; return 0; }
notfound() { echo "Error from server (NotFound): $1 not found" >&2; exit 1; }
absent() {
  case "$args" in
    *--ignore-not-found*) exit 0 ;;
  esac
  notfound "$1"
}
verb=${1:-}
shift 2>/dev/null || true
args="$*"
case "$verb" in
  get)
    if [ -n "${FAKE_GET_FAIL:-}" ]; then
      case "$args" in
        *"${FAKE_GET_FAIL}"*)
          echo "${FAKE_GET_FAIL_MSG:-Error from server: etcdserver: request timed out}" >&2; exit 1 ;;
      esac
    fi
    if [ -n "${FAKE_WARN_STDERR:-}" ]; then
      echo "Warning: cluster.x-k8s.io/v1beta1 MachineDeployment is deprecated" >&2
    fi
    case "$args" in
      *helmrelease*--all-namespaces*)
        cat "$FAKE_HR_LIST"; exit 0 ;;
      *helmrelease*kubernetes-nodes-*)
        notfound "helmreleases.helm.toolkit.fluxcd.io" ;;  # child HR absent -> create path
    esac
    # tokenize: $1=resource, $2=object-name (or a flag for list queries)
    # shellcheck disable=SC2086
    set -- $args
    res=$1; name=$2
    # Real kubectl rejects a name containing '/' client-side, before any API
    # call, deterministically -- reproduce that so the migration provably never
    # issues a read for a composed name that cannot exist.
    case "$name" in
      */*)
        echo "error: there is no need to specify a resource type as a separate argument when passing arguments in resource/name form" >&2
        exit 1 ;;
    esac
    case "$res" in
      kubevirtmachinetemplate*)
        case "$name" in
          -*|"")                        # KMT range/list query
            [ -n "${FAKE_KMT_NAMES:-}" ] && printf '%s\n' "$FAKE_KMT_NAMES"
            exit 0 ;;
        esac ;;
    esac
    # Named-object get. When FAKE_OBJS is set (adoption-path tests), look the
    # object up there: a matching line "<res> <name> <release-name>" means present.
    # Without FAKE_OBJS every named get is "absent" (value-mapping tests rely on
    # adopt_one short-circuiting on absent).
    if [ -n "${FAKE_OBJS:-}" ] && [ -f "$FAKE_OBJS" ]; then
      line=$(grep -E "^${res}[[:space:]]+${name}([[:space:]]|$)" "$FAKE_OBJS" || true)
      [ -z "$line" ] && absent "${res} \"${name}\""         # absent
      rel=$(printf '%s\n' "$line" | awk '{print $3}')
      case "$args" in
        *jsonpath*release-name*) printf '%s' "$rel"; exit 0 ;;
        *" -o json"*)
          printf '{"metadata":{"annotations":{"meta.helm.sh/release-name":"%s"}}}' "$rel"; exit 0 ;;
        *" -o name"*)   printf '%s/%s\n' "$res" "$name"; exit 0 ;;
        *) exit 0 ;;                                        # existence check
      esac
    fi
    absent "${res} \"${name}\"" ;;      # default: absent
  apply)
    payload=$(cat)
    if printf '%s' "$payload" | grep -q 'HelmRelease'; then
      # Reject an RFC-1123-invalid metadata.name the way the real apiserver would,
      # so the migration's name-validity guard is exercised: without it the script
      # would reach this apply and exit non-zero, deadlocking the upgrade.
      hrname=$(printf '%s' "$payload" | jq -r '.metadata.name // ""' 2>/dev/null)
      if ! printf '%s' "$hrname" | grep -qE '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'; then
        echo "The HelmRelease \"$hrname\" is invalid: metadata.name: Invalid value: \"$hrname\": a lowercase RFC 1123 label is required" >&2
        exit 1
      fi
      printf '%s' "$payload" > "$FAKE_CHILD_HR"; log "APPLY-HR"
    elif printf '%s' "$payload" | grep -q 'ConfigMap'; then
      if printf '%s' "$payload" | grep -q 'cozystack-migration-56-unadopted'; then
        # FAKE_UNADOPTED_CM_FAIL exercises the best-effort path: a permanent
        # failure publishing the record must NOT abort the migration.
        if [ -n "${FAKE_UNADOPTED_CM_FAIL:-}" ]; then
          echo "Error from server: ConfigMap \"cozystack-migration-56-unadopted\" is invalid: exceeds object size limit" >&2
          exit 1
        fi
        log "APPLY-UNADOPTED-CM"
      else
        log "STAMP"
      fi
    else
      log "APPLY-OTHER"
    fi
    exit 0 ;;
  annotate) log "annotate $args"; exit 0 ;;
  label)    log "label $args"; exit 0 ;;
  create)
    # Only `create configmap cozystack-migration-56-unadopted --from-file ...
    # --dry-run=client -o yaml` is used; emit a minimal ConfigMap manifest so the
    # piped `apply --filename -` receives it and logs APPLY-UNADOPTED-CM.
    case "$args" in
      *configmap*cozystack-migration-56-unadopted*)
        printf 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: cozystack-migration-56-unadopted\n' ;;
    esac
    exit 0 ;;
  *) exit 0 ;;
esac
