#!/usr/bin/env bash
set -uo pipefail

# Validates JSON and exists if invalid
#
# Args: $1 file path | $2 soft-fail only
brp_json_validate()
{
  local jq_out
  jq_out=$(jq empty "$1" 2>&1)
  if [ $? -eq 0 ]; then
    pr_dbg "JSON file \"%s\" is valid" "$1"
    return 0
  fi

  if [[ ${2: 0} -eq 1 ]]; then
    pr_err "JSON file \"%s\" is invalid:\n\n%s" "$1" "${jq_out}"
  else
    pr_crit "JSON file \"%s\" is invalid:\n\n%s" "$1" "${jq_out}"
  fi
  return 1
}

# Checks if a given key existing in JSON
#
# Args: $1 file path | $2 field
brp_json_has_field()
{
  ${JQ_PATH} -e -r ".$2|select(0)" "$1" &>/dev/null
  if [[ $? -eq 0 ]]; then
    echo "1"
  else
    echo "0"
  fi
}

# Gets a single value of a key or exist on error
#
# Args: $1 file path | $2 field | $3 empty-on-error [default=0]
brp_json_get_field()
{
  local field_val;
  field_val=$(${JQ_PATH} -e -r ".$2" "$1")
  # "1 if the last output value was either false or null"
  if [ $? -le 1 ]; then
    echo $field_val
    return 0
  fi

  if [ "${3:-'0'}" != 1 ]; then
    pr_crit "Field \"$2\" doesn't existing in $1"
  fi
}

# Gets a list of keys from the object
#
# This function GUARANTEES that keys are returned in their order in file
# Do NOT try to change it to "keys" to support older JQs as it will break other things (e.g. brp_read_ordered_kv)
#
# Args: $1 file path | $2 field | $3 return array
brp_json_get_keys()
{
  local -n __json_return=$3

  local out;
  out=$("${JQ_PATH}" -r -e ".${2}|keys_unsorted|.[]" "${1}" 2>&1)
  if [ $? -ne 0 ]; then
    pr_crit "Failed extract K=>V pairs from %s:.%s\n\n%s" "${1}" "${2}" "${out}"
  fi

  readarray -t __json_return <<< "${out}"
}

# Gets a new line-separated list of values from array or exist on error
#
# TODO: this is fugly - it should use references
#
# Args: $1 file path | $2 field | $3 empty-on-error [default=0]
brp_json_get_array_values()
{
  local field_val;
  field_val=$(${JQ_PATH} -e -r ".${2} | .[]" "${1}")
  local jq_exit=$?

  # "1 if the last output value was either false or null", 4 if it was empty... we hate it
  if [[ jq_exit -le 1 ]] || [[ jq_exit -eq 4 ]] ; then
    echo "${field_val}"
    return 0
  fi

  if [ "${3:-'0'}" != 1 ]; then
    pr_crit "Field \"$2\" doesn't existing in $1"
  fi
}

# Reads array values and outputs them as a delimiter-separated string
#
# Args: $1 file path | $2 field | $3 empty-on-error [default=0] | $4 separator [default=,]
rpt_json_get_array_values_flat()
{
  local -a _entries
  local _entries_txt

  if [[ -z ${3+x} ]]; then
    local ignore_err=0
  else
    local ignore_err="${3}"
  fi
  if [[ -z ${4+x} ]]; then
    local separator=','
  else
    local separator="${4}"
  fi

  _entries_txt="$(brp_json_get_array_values "${1}" "${2}" "${ignore_err}")"
  local get_vals_exit=$?
  if [[ $? -ne 0 ]]; then
    echo "${_entries_txt}"
    return 1
  fi

  readarray -t _entries <<< "${_entries_txt}"
  _entries_txt=''
  for entry in ${_entries[@]+"${_entries[@]}"}; do
    if [[ ! -z "${_entries_txt}" ]]; then
      _entries_txt+="${separator}"
    fi

    _entries_txt+="${entry}"
  done

  echo "${_entries_txt}"
}

# Check if passed string is "null" or ""
#
# Args: $1 string to check
brp_json_noe()
{
  if [ "$1" == "null" ] || [ -z "$1" ]; then
    return 0
  else
    return 1
  fi
}

# Reads JSON k=>v into bash associative array
#
# WARNING: THIS WILL **NOT** PRESERVE THE ORIGINAL ORDER OF VALUES!
#          Bash stores associative arrays in hashes, so that order is lost. This is perfectly fine for some scenarios
#          (e.g. list of files to copy) but disastrous in others (e.g. GRUB commands). If you want the order use
#          brp_read_ordered_kv() instead.
#
# Args: $1 JSON file | $2 JSON key | $3 array to read to
brp_read_kv_to_array()
{
  # kv_pair used dynamically in kv_extractor
  # shellcheck disable=SC2034
  local -n __json_kv_pairs=$3
  local kv_extractor='.'"${2}"'|to_entries|map("[\(.key|@sh)]=\(.value|@sh) ")|"__json_kv_pairs=(" + add + ")"';

  local out;
  out=$("${JQ_PATH}" -r -e "${kv_extractor}" "${1}" 2>&1)
  if [ $? -ne 0 ]; then
    pr_crit "Failed extract K=>V pairs from %s:.%s\n\n%s" "${1}" "${2}" "${out}"
  fi

  # if you get a useless BASH error "invalid arithmetic operator" here check the variable you've passed (it must be -A)
  eval "$out"
}

# Reads JSON k=>v preserving order of keys
#
# This function preserves order of keys from the original array. Since Bash uses hash maps for associative arrays two
# array (keys+values) must be used. See https://stackoverflow.com/a/29161460
#
# To use it do:
# local -a keys
# local -A values
# brp_read_ordered_kv 'file.json' 'super.entries' keys values   # do NOT quote keys or values
# for key in "${keys[@]}"; do echo "${values[$key]}"; done
#
# Args: $1 JSON file | $2 JSON key | $3 normal keys array | $4 associative values array
brp_read_ordered_kv()
{
  local -n __json_keys=$3
  local -n __json_values=$4
  brp_json_get_keys "${1}" "${2}" __json_keys
  brp_read_kv_to_array "${1}" "${2}" __json_values # we can reuse code to read k=>v pairs as we need them anyway
}
