#!/usr/bin/env bash

validate_command() {
  local PERSON="$1"
  local cmd="$2"
  shift 2
  local tokens=("$@")
  local yaml="access.yml"

  # Check if fixedArgsCommands.<cmd> exists
  local is_fixed
  is_fixed="$(yq e ".\"$PERSON\".fixedArgsCommands | has(\"$cmd\")" "$yaml")"
  # Check if multiArgsCommands.<cmd> exists
  local is_multi
  is_multi="$(yq e ".\"$PERSON\".multiArgsCommands | has(\"$cmd\")" "$yaml")"

  if [[ "$is_fixed" != "true" && "$is_multi" != "true" ]]; then
    echo "ERROR: Command '$cmd' not allowed for $PERSON" >&2
    return 1
  fi

  # Exclude flags from positional args
  local args=()
  for tok in "${tokens[@]}"; do
    [[ "$tok" == -* ]] && continue
    args+=("$tok")
  done

  if [[ "$is_fixed" == "true" ]]; then
    mapfile -t allowed < <(yq e ".\"$PERSON\".fixedArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null)
    local n_allowed="${#allowed[@]}"
    if [[ $n_allowed -eq 0 ]]; then
      # zero-arg command
      if [[ ${#args[@]} -ne 0 ]]; then
        echo "ERROR: Command '$cmd' takes no arguments" >&2
        return 1
      fi
    else
      # depth is 1: only one of the allowed choices must be present
      if [[ ${#args[@]} -ne 1 ]]; then
        echo "ERROR: Command '$cmd' requires exactly 1 argument: (${allowed[*]})" >&2
        return 1
      fi
      local found=0
      for want in "${allowed[@]}"; do
        [[ "${args[0]}" == "$want" ]] && found=1 && break
      done
      if [[ $found -eq 0 ]]; then
        echo "ERROR: Invalid argument '${args[0]}' for '$cmd'; allowed: (${allowed[*]})" >&2
        return 1
      fi
    fi
    return 0
  fi

  if [[ "$is_multi" == "true" ]]; then
    mapfile -t allowed < <(yq e ".\"$PERSON\".multiArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null)
    local n_allowed="${#allowed[@]}"
    if [[ ${#args[@]} -lt 1 || ${#args[@]} -gt $n_allowed ]]; then
      echo "ERROR: Command '$cmd' requires 1 to $n_allowed arguments: (${allowed[*]})" >&2
      return 1
    fi

    # Order doesn't matter, but all must be unique and from allowed.
    # Build a set of allowed args.
    declare -A allowed_set=()
    for want in "${allowed[@]}"; do allowed_set["$want"]=1; done

    declare -A seen=()
    for a in "${args[@]}"; do
      if [[ -z "${allowed_set[$a]+_}" ]]; then
        echo "ERROR: Invalid argument '$a' for '$cmd'; allowed: (${allowed[*]})" >&2
        return 1
      fi
      if [[ -n "${seen[$a]+_}" ]]; then
        echo "ERROR: Duplicate argument '$a' for '$cmd'" >&2
        return 1
      fi
      seen["$a"]=1
    done
    return 0
  fi
}