#!/usr/bin/env bash
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"

fn-proxy-escape-extended-sed() {
  # Escaped delimiter is '/'
  local input="$1"
  echo "$input" | sed -E 's#[][()\.^$?*+{}|/]#\\&#g'
}

fn-proxy-get-labels-file-path() {
  declare desc="return proxy labels config file path for specified proxy"
  declare PROXY="$1" APP="$2"
  local PROXY_FILE="${DOKKU_LIB_ROOT}/config/${PROXY}/${APP}/labels"

  echo "$PROXY_FILE"
}

fn-proxy-get-readiness-healthcheck() {
  declare desc="outputs the first web readiness healthcheck with a path from the app's app.json as json"
  declare APP="$1"
  local app_json

  app_json="$(plugn trigger app-json-get-content "$APP" 2>/dev/null || echo "{}")"
  echo "$app_json" | jq -c '.healthchecks.web // [] | map(select(.type == "readiness" and .path != null and .path != "")) | .[0] // empty'
}

fn-proxy-healthcheck-path-is-valid() {
  declare desc="checks that an app.json healthcheck path is absolute and free of whitespace, control, quote, and backslash characters"
  declare HC_PATH="$1"

  [[ "$HC_PATH" == /* ]] || return 1
  [[ "$HC_PATH" =~ [[:space:][:cntrl:]\"\'\\] ]] && return 1
  return 0
}

fn-proxy-healthcheck-header-is-valid() {
  declare desc="checks that an app.json healthcheck header name is a token and its value is free of control, quote, and backslash characters"
  declare NAME="$1" VALUE="$2"

  [[ "$NAME" =~ ^[A-Za-z0-9!#$%\&*+.^_\`\|~-]+$ ]] || return 1
  [[ "$VALUE" =~ [[:cntrl:]\"\'\\] ]] && return 1
  return 0
}

fn-proxy-get-healthcheck-host() {
  declare desc="outputs the host header for a healthcheck, preferring an app.json Host header over the first app domain"
  declare APP="$1" HEALTHCHECK="$2"
  local domain hc_host
  local -a domains

  hc_host="$(echo "$HEALTHCHECK" | jq -r '.httpHeaders // [] | map(select((.name | ascii_downcase) == "host")) | .[0].value // empty')"
  if [[ -n "$hc_host" ]]; then
    echo "$hc_host"
    return
  fi

  read -r -d '' -a domains <<<"$(plugn trigger domains-list "$APP")" || true
  for domain in "${domains[@]}"; do
    if [[ "$domain" == "localhost" ]] || [[ "$domain" == '*' ]]; then
      continue
    fi

    echo "$domain"
    return
  done
}

fn-proxy-get-healthcheck-headers() {
  declare desc="outputs the tab-separated name and value of each non-Host app.json healthcheck header"
  declare HEALTHCHECK="$1"

  echo "$HEALTHCHECK" | jq -r '.httpHeaders // [] | map(select((.name | ascii_downcase) != "host")) | .[] | [.name, .value] | @tsv'
}

fn-proxy-healthcheck-scheme-is-valid() {
  declare desc="checks that an app.json healthcheck scheme is either http or https"
  declare HC_SCHEME="$1"

  [[ -z "$HC_SCHEME" ]] && return 0
  [[ "${HC_SCHEME,,}" == "http" ]] || [[ "${HC_SCHEME,,}" == "https" ]]
}

fn-proxy-display-labels() {
  declare desc="print user-set app container labels for specified proxy"
  declare PROXY="$1" APP="$2"
  local proxy_labels_file_path="$(fn-proxy-get-labels-file-path "$PROXY" "$APP")"
  if [[ -s "$proxy_labels_file_path" ]]; then
    dokku_log_info2_quiet "${APP} ${PROXY} labels"
    while read -r line; do
      [[ -z "$line" ]] && continue

      case "$line" in
        \#*)
          continue
          ;;

        *)
          dokku_log_verbose "$line"
          ;;
      esac
    done <"$proxy_labels_file_path"
  else
    dokku_log_warn_quiet "No ${PROXY} labels set for ${APP}"
  fi
}

fn-proxy-show-label() {
  declare desc="print single user-set app container label for specified proxy"
  declare PROXY="$1" APP="$2" PASSED_LABEL_NAME="$3"

  local proxy_labels_file_path="$(fn-proxy-get-labels-file-path "$PROXY" "$APP")"
  if [[ ! -s "$proxy_labels_file_path" ]]; then
    return
  fi

  local all_proxy_labels="$(<"$proxy_labels_file_path")"
  local escaped_label_name="$(fn-proxy-escape-extended-sed "$PASSED_LABEL_NAME")"
  local proxy_label_value="$(echo -e "${all_proxy_labels}" | sed -E -n "s/^${escaped_label_name}=(.*)\$/\1/p")"

  echo "$proxy_label_value"
}

fn-proxy-labels-add() {
  declare desc="adds a container label for the specified proxy, expecting the caller to hold the proxy property lock"
  declare PROXY="$1" APP="$2" LABEL_NAME="$3" LABEL_VALUE="$4"
  local proxy_labels_file_path="$(fn-proxy-get-labels-file-path "$PROXY" "$APP")"
  local all_proxy_labels=""

  if [[ -s "$proxy_labels_file_path" ]]; then
    all_proxy_labels="$(fn-proxy-labels-without "$proxy_labels_file_path" "$LABEL_NAME")"
  fi

  all_proxy_labels="${all_proxy_labels}"$'\n'"${LABEL_NAME}=${LABEL_VALUE}"
  echo -e "${all_proxy_labels}" | sed '/^$/d' | sort -u | fn-proxy-write-labels-file "$proxy_labels_file_path"
}

fn-proxy-labels-remove() {
  declare desc="removes a container label for the specified proxy, expecting the caller to hold the proxy property lock"
  declare PROXY="$1" APP="$2" LABEL_NAME="$3"
  local proxy_labels_file_path="$(fn-proxy-get-labels-file-path "$PROXY" "$APP")"

  if [[ ! -s "$proxy_labels_file_path" ]]; then
    return
  fi

  fn-proxy-labels-without "$proxy_labels_file_path" "$LABEL_NAME" | fn-proxy-write-labels-file "$proxy_labels_file_path"
}

fn-proxy-labels-without() {
  declare desc="prints the sorted labels in a proxy labels file without the named label"
  declare LABELS_FILE="$1" LABEL_NAME="$2"
  local all_proxy_labels="$(<"$LABELS_FILE")"
  local escaped_label_name="$(fn-proxy-escape-extended-sed "$LABEL_NAME")"

  echo -e "${all_proxy_labels}" | sed -E "s/^${escaped_label_name}=.*\$//" | sed '/^$/d' | sort -u
}

fn-proxy-write-labels-file() {
  declare desc="atomically replaces a proxy labels file with the contents of stdin"
  declare LABELS_FILE="$1"
  local tmp_file

  tmp_file="$(mktemp "$(dirname "$LABELS_FILE")/.labels.XXXXXX")"
  if ! cat >"$tmp_file"; then
    rm -f "$tmp_file"
    return 1
  fi

  if [[ -f "$LABELS_FILE" ]]; then
    chmod --reference="$LABELS_FILE" "$tmp_file"
  else
    chmod 0644 "$tmp_file"
  fi
  mv -f "$tmp_file" "$LABELS_FILE"
}
