#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)"
# GIT_CONFIG_VALUE_1 below is a `!`-prefixed credential.helper, which git
# re-parses through `sh -c`: a space in the install path breaks the command and
# a `$(...)` in it executes. Quoting is not enough — allowlist the characters.
case "$SCRIPT_DIR" in
  *[!A-Za-z0-9._/-]*) echo "bot-env: install path contains an unsafe character; refusing to emit a shell-evaluated credential helper" >&2; exit 1 ;;
esac

BOT_NAME="acme-agent[bot]"
BOT_EMAIL="<BOT_UID>+acme-agent[bot]@users.noreply.github.com"

# One installation of the App per GitHub account (org or user), so account ->
# installation id is a total function over the accounts this setup serves.
# One `account:id` pair per line, account names lowercase. An account absent
# here gets the personal verdict, never another entry's id: a fallback would
# mint a *valid* token for the wrong installation, which 404s on the repo
# instead of failing at auth.
ORG_INSTALLS="
acme:REPLACE
"
install_id_for_org() {
  local _line
  for _line in $ORG_INSTALLS; do
    case "$_line" in "$1":*) printf '%s' "${_line#*:}"; return 0 ;; esac
  done
  return 1
}

# Validate the map before any verdict: a malformed or duplicate entry is a
# config error that must abort the command, never a silent first-entry win —
# a typo'd duplicate would mint a *valid* token for the wrong installation.
# The id shape is allowlisted here because it is emitted inside single quotes
# below; numeric enforcement (including the unreplaced placeholder) is
# bot-token's.
_seen_orgs=""
for _entry in $ORG_INSTALLS; do
  case "$_entry" in
    *:*) ;;
    *) echo "bot-env: ORG_INSTALLS entry '${_entry}' is not account:id; refusing to run" >&2; exit 1 ;;
  esac
  _org="${_entry%%:*}"
  _id="${_entry#*:}"
  case "$_org" in
    ""|*[!a-z0-9-]*) echo "bot-env: ORG_INSTALLS account '${_org}' must be lowercase a-z, 0-9, or -; refusing to run" >&2; exit 1 ;;
  esac
  case "$_id" in
    ""|*[!A-Za-z0-9]*) echo "bot-env: ORG_INSTALLS id for '${_org}' has an unsafe shape; refusing to run" >&2; exit 1 ;;
  esac
  case "$_seen_orgs" in
    *"|${_org}|"*) echo "bot-env: ORG_INSTALLS has a duplicate entry for '${_org}'; refusing to run" >&2; exit 1 ;;
  esac
  _seen_orgs="${_seen_orgs}|${_org}|"
done
[ -n "$_seen_orgs" ] || { echo "bot-env: ORG_INSTALLS is empty; refusing to run" >&2; exit 1; }

# Lowercased host of a remote URL in either the scheme form
# (`ssh://[user@]host[:port]/path`) or the scp form (`[user@]host:path`).
# git recognizes the scp form only when there is a colon with no slash before
# it; anything else — a local path (even one named `github.com`), or the bare
# remote name git reports when a push-only remote has no fetch URL — has no
# host and yields the empty string.
url_host_lc() {
  local u="$1" rest auth host=""
  case "$u" in
    *://*) rest="${u#*://}"; auth="${rest%%/*}"; host="${auth#*@}"; host="${host%%:*}" ;;
    *:*) case "${u%%:*}" in */*) ;; *) rest="${u#*@}"; host="${rest%%:*}" ;; esac ;;
  esac
  printf '%s' "$host" | tr '[:upper:]' '[:lower:]'
}

verdict=personal
matched_orgs=""
# Every remote name that carries a fetch URL, on any host: the effective-URL
# check below asks git for the fetch direction only where one is configured,
# because git answers a push-only remote's fetch URL with the remote's name.
names_with_url=()
# Raw github.com remote values seen in this repo (deduplicated), their HTTPS
# targets, and the remote names that carry them. On a bot verdict every raw
# value gets an exact rewrite pair, and every name is checked for its
# effective transport (see the emission below). Arrays, not delimited
# records: any character git accepts in a URL must survive verbatim.
github_raw=()
github_target=()
github_names=()
rc=0
# Only stderr is captured, under a fixed locale: git exits 128 on *every*
# fatal error, and only the "not a git repository" one is a definitive
# personal answer. A corrupt config, an unsupported repository format,
# malformed inherited GIT_CONFIG_* state, or dubious ownership also exit 128,
# and none of them can rule out org work.
probe_err="$(LC_ALL=C git rev-parse --is-inside-work-tree 2>&1 >/dev/null)" || rc=$?
if [ "$rc" -eq 0 ]; then
  raw_remotes="$(mktemp)"
  worktree_remotes="$(mktemp)"
  trap 'rm -f "$raw_remotes" "$worktree_remotes"' EXIT
  remote_rc=0
  git config --local --null --get-regexp '^remote\..*\.(url|pushurl)$' >"$raw_remotes" 2>/dev/null || remote_rc=$?
  if [ "$remote_rc" -eq 1 ] && [ ! -s "$raw_remotes" ]; then
    remote_rc=0
  fi
  if [ "$remote_rc" -eq 0 ]; then
    worktree_enabled=""
    worktree_flag_rc=0
    worktree_enabled="$(git config --local --bool --get extensions.worktreeConfig 2>/dev/null)" || worktree_flag_rc=$?
    if [ "$worktree_flag_rc" -eq 0 ] && [ "$worktree_enabled" = true ]; then
      worktree_rc=0
      git config --worktree --null --get-regexp '^remote\..*\.(url|pushurl)$' >"$worktree_remotes" 2>/dev/null || worktree_rc=$?
      if [ "$worktree_rc" -eq 0 ]; then
        cat "$worktree_remotes" >>"$raw_remotes"
      elif [ "$worktree_rc" -ne 1 ] || [ -s "$worktree_remotes" ]; then
        remote_rc="$worktree_rc"
      fi
    elif [ "$worktree_flag_rc" -ne 0 ] && [ "$worktree_flag_rc" -ne 1 ]; then
      remote_rc="$worktree_flag_rc"
    fi
  fi
  if [ "$remote_rc" -eq 0 ]; then
    if [ ! -s "$raw_remotes" ]; then
      verdict=bot
      echo "bot-env: no raw remote URLs in $PWD; ambiguous, using the bot identity" >&2
    else
      empty_remote=0
      while IFS= read -r -d '' remote_record; do
        case "$remote_record" in
          *$'\n'*) url="${remote_record#*$'\n'}"; key="${remote_record%%$'\n'*}" ;;
          *) empty_remote=1; continue ;;
        esac
        if [ -z "$url" ]; then
          empty_remote=1
          continue
        fi
        # `remote.<name>.url` / `remote.<name>.pushurl`; the name may itself
        # contain dots, so strip exactly the final property, never a suffix.
        name="${key#remote.}"; name="${name%.*}"
        case "$key" in
          *.url)
            seen=0
            for existing in "${names_with_url[@]+"${names_with_url[@]}"}"; do
              [ "$existing" = "$name" ] && { seen=1; break; }
            done
            [ "$seen" -eq 0 ] && names_with_url+=("$name") ;;
        esac
        # Parse the raw value down to its authority and path: host decides
        # GitHub-ness (`url_host_lc`, which treats a colonless or
        # slash-before-colon value as a local path with no host), the first
        # path segment decides the account. A leading slash in an scp-style
        # path (`git@github.com:/acme/x.git`) is tolerated by GitHub, so it is
        # stripped before both decisions.
        host_lc="$(url_host_lc "$url")"
        [ "$host_lc" = github.com ] || continue
        case "$url" in
          *://*)
            rest="${url#*://}"
            case "$rest" in */*) path="${rest#*/}" ;; *) path="" ;; esac
            ;;
          *)
            path="${url#*:}"
            ;;
        esac
        path="${path#/}"
        # The raw value is emitted verbatim inside single quotes as a
        # url.<base>.insteadOf key and value; refuse anything that cannot
        # travel that way rather than route with a partial identity.
        case "$url" in
          *"'"*|*'"'*|*'\'*|*[[:space:]]*|*[[:cntrl:]]*)
            echo "bot-env: raw remote URL in $PWD contains a character that cannot be emitted safely; refusing to run with undetermined identity" >&2
            exit 1 ;;
        esac
        seen=0
        for existing in "${github_raw[@]+"${github_raw[@]}"}"; do
          [ "$existing" = "$url" ] && { seen=1; break; }
        done
        if [ "$seen" -eq 0 ]; then
          github_raw+=("$url")
          github_target+=("https://github.com/${path}")
        fi
        seen=0
        for existing in "${github_names[@]+"${github_names[@]}"}"; do
          [ "$existing" = "$name" ] && { seen=1; break; }
        done
        [ "$seen" -eq 0 ] && github_names+=("$name")
        org_segment_lc="$(printf '%s' "${path%%/*}" | tr '[:upper:]' '[:lower:]')"
        if install_id_for_org "$org_segment_lc" >/dev/null; then
          verdict=bot
          case "$matched_orgs" in
            *"|${org_segment_lc}|"*) ;;
            *) matched_orgs="${matched_orgs}|${org_segment_lc}|" ;;
          esac
        fi
      done <"$raw_remotes"
      if [ "$empty_remote" -eq 1 ] && [ "$verdict" != bot ]; then
        verdict=bot
        echo "bot-env: empty raw remote URL in $PWD; ambiguous, using the bot identity" >&2
      fi
    fi
  else
    verdict=bot
    echo "bot-env: raw remote query failed in $PWD; using the bot identity" >&2
  fi
elif [ "$rc" -eq 128 ]; then
  # Anchored to the first line: a malformed inherited config key can echo the
  # phrase inside an unrelated diagnostic, and that is not git's answer.
  case "$probe_err" in
    "fatal: not a git repository"*) ;;
    *)
      verdict=bot
      echo "bot-env: git probe failed (exit 128: ${probe_err%%$'\n'*}) in $PWD; ambiguous, using the bot identity" >&2 ;;
  esac
else
  verdict=bot
  echo "bot-env: git probe failed (exit $rc) in $PWD; ambiguous, using the bot identity" >&2
fi

if [ "$verdict" != bot ]; then
  cat <<'EOF'
unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
unset GIT_CONFIG_COUNT
unset GH_TOKEN
unset BOT_INSTALL_ID
EOF
  # In a reused shell, bot-env inherits any exported GIT_CONFIG_* vars, so it
  # can enumerate exactly what exists and emit portable unset lines for them.
  for v in $(compgen -v | grep -E '^GIT_CONFIG_(KEY|VALUE)_[0-9]+$' || true); do
    echo "unset $v"
  done
  exit 0
fi

# Installations are per account: two mapped accounts in one repo's remotes
# would need two different tokens, so there is no single right installation.
matched_org=""
n_matched=0
for o in $(printf '%s' "$matched_orgs" | tr '|' ' '); do
  n_matched=$((n_matched + 1))
  matched_org="$o"
done
if [ "$n_matched" -gt 1 ]; then
  echo "bot-env: remotes match more than one mapped account in $PWD; refusing to run with undetermined installation" >&2
  exit 1
fi

# The id's emission-safe shape was enforced by the map validation above.
bot_install_id=""
if [ -n "$matched_org" ]; then
  bot_install_id="$(install_id_for_org "$matched_org")"
fi

# A mint that "succeeds" with empty output is as dangerous as a crash: gh treats
# an empty GH_TOKEN as unset and falls back to the personal stored credentials.
# The mint must see the same installation selection the emitted env exports —
# and never a stale BOT_INSTALL_ID inherited from a previously visited repo.
if [ -n "$matched_org" ]; then
  token="$(BOT_INSTALL_ID="$bot_install_id" "$SCRIPT_DIR/bot-token")" || token=""
else
  token="$(env -u BOT_INSTALL_ID "$SCRIPT_DIR/bot-token")" || token=""
fi
[ -n "$token" ] || token="BOT-TOKEN-MINT-FAILED"

# Under a bot verdict every github.com transport in this command must go
# through HTTPS, and so through the host-gated bot credential helper: a remote
# left on SSH would push with the personal key under the bot's authorship.
# git resolves insteadOf/pushInsteadOf by longest matching prefix across every
# config scope (first-read wins a tie, and the user's own config is read
# first), so each rewrite is emitted twice — pushInsteadOf is consulted before
# insteadOf for pushes — and at three lengths: host-wide pairs, which cover
# the canonical forms and any URL typed on the command line; identity pairs on
# each mapped account's https prefix, which outrank a host-wide force-SSH rule
# in the user's config for that account's URLs; and one exact pair per raw
# remote value, which outranks any prefix rule and covers mixed-case and
# ssh:// spellings verbatim.
pair_base=()
pair_value=()
add_pair() { pair_base+=("$1"); pair_value+=("$2"); }
add_pair 'https://github.com/' 'git@github.com:'
add_pair 'https://github.com/' 'github.com:'
add_pair 'https://github.com/' 'ssh://git@github.com/'
add_pair 'https://github.com/' 'https://github.com/'
for _line in $ORG_INSTALLS; do
  add_pair "https://github.com/${_line%%:*}/" "https://github.com/${_line%%:*}/"
done
i=0
while [ "$i" -lt "${#github_raw[@]}" ]; do
  add_pair "${github_target[$i]}" "${github_raw[$i]}"
  i=$((i + 1))
done

# What rewrites cannot win is a rule in the user's own config that matches a
# remote's complete URL: equal length, read first, it keeps the remote on SSH.
# So ask git, with exactly the rewrites about to be emitted, where each
# github.com remote actually resolves, and refuse to route if any direction
# still leaves github.com over anything but HTTPS — a push there would ride
# the personal key under the bot's authorship. URLs typed on the command line
# are outside this check; the host-wide and account pairs are their only cover.
cfg=()
i=0
while [ "$i" -lt "${#pair_base[@]}" ]; do
  cfg+=(-c "url.${pair_base[$i]}.insteadOf=${pair_value[$i]}" -c "url.${pair_base[$i]}.pushInsteadOf=${pair_value[$i]}")
  i=$((i + 1))
done
for name in "${github_names[@]+"${github_names[@]}"}"; do
  has_url=0
  for existing in "${names_with_url[@]+"${names_with_url[@]}"}"; do
    [ "$existing" = "$name" ] && { has_url=1; break; }
  done
  for direction in "" --push; do
    [ "$direction" = "" ] && [ "$has_url" -eq 0 ] && continue
    effective="$(git "${cfg[@]}" remote get-url --all $direction "$name" 2>/dev/null)" || {
      echo "bot-env: could not resolve the effective URL of remote '${name}' in $PWD; refusing to run with undetermined transport" >&2
      exit 1
    }
    while IFS= read -r resolved; do
      [ -n "$resolved" ] || continue
      # Only github.com destinations are held to HTTPS; a remote that pushes
      # to another host is that host's business and the helper ignores it.
      case "$resolved" in [Hh][Tt][Tt][Pp][Ss]://*) continue ;; esac
      [ "$(url_host_lc "$resolved")" = github.com ] || continue
      echo "bot-env: remote '${name}' still resolves to ${resolved} after the bot rewrites (a rule in your own git config matches its complete URL and wins the tie); refusing to run with a github.com remote outside the bot token" >&2
      exit 1
    done <<EOF3
$effective
EOF3
  done
done

cat <<EOF
export GIT_AUTHOR_NAME='${BOT_NAME}'
export GIT_AUTHOR_EMAIL='${BOT_EMAIL}'
export GIT_COMMITTER_NAME='${BOT_NAME}'
export GIT_COMMITTER_EMAIL='${BOT_EMAIL}'
export GIT_CONFIG_KEY_0='credential.helper'
export GIT_CONFIG_VALUE_0=''
export GIT_CONFIG_KEY_1='credential.helper'
export GIT_CONFIG_VALUE_1='!${SCRIPT_DIR}/git-credential-bot'
export GIT_CONFIG_KEY_2='commit.gpgsign'
export GIT_CONFIG_VALUE_2='false'
EOF
idx=3
i=0
while [ "$i" -lt "${#pair_base[@]}" ]; do
  cat <<EOF
export GIT_CONFIG_KEY_${idx}='url.${pair_base[$i]}.insteadOf'
export GIT_CONFIG_VALUE_${idx}='${pair_value[$i]}'
export GIT_CONFIG_KEY_$((idx + 1))='url.${pair_base[$i]}.pushInsteadOf'
export GIT_CONFIG_VALUE_$((idx + 1))='${pair_value[$i]}'
EOF
  idx=$((idx + 2))
  i=$((i + 1))
done

if [ -n "$matched_org" ]; then
  echo "export BOT_INSTALL_ID='${bot_install_id}'"
else
  echo "unset BOT_INSTALL_ID"
fi
cat <<EOF
export GIT_CONFIG_COUNT=${idx}
export GH_TOKEN='${token}'
EOF
