#!/bin/bash
#
# account-resolver.sh
# Discovers GitHub / Jira / Bitbucket / Confluence accounts from macOS Keychain
# and emits a JSON inventory consumed by the multi-agent picker.
#
# Each account is identified by a *prefix*  -  the keychain service-name segment
# that precedes the provider segment. Examples:
#   ${USER}_Github_Access_Token        -> prefix=${USER}
#   ${USER}_Tktech_Github_Access_Token -> prefix=${USER}_Tktech
#   personal_Github_Auth_Token        -> prefix=personal
#
# Usage:
#   ./account-resolver.sh             # JSON array
#   ./account-resolver.sh --pretty    # indented JSON
#   ./account-resolver.sh --providers github   # only accounts with github token
#
# Output schema (per account):
#   { id, label, prefix, providers[], tokens{}, jiraHost, bitbucketHost, isWork }

set -euo pipefail

PRETTY=0
FILTER=""
while [ $# -gt 0 ]; do
  case "$1" in
    --pretty) PRETTY=1; shift ;;
    # `shift 2` on a trailing `--providers` (no value) fails under `set -e` and
    # aborts silently. Consume one at a time so a missing value is an empty
    # filter, not a crash.
    --providers) FILTER="${2:-}"; shift; [ $# -gt 0 ] && shift ;;
    *) shift ;;
  esac
done

prefs_file="$HOME/.claude/multi-agent-preferences.json"

# Read default host overrides from prefs.global.hosts (no hardcoded corporate hostnames).
prefs_jira_host=$(python3 -c '
import json,sys
try: print(json.load(open(sys.argv[1])).get("global",{}).get("hosts",{}).get("jira") or "")
except: print("")' "$prefs_file" 2>/dev/null)
prefs_bb_host=$(python3 -c '
import json,sys
try: print(json.load(open(sys.argv[1])).get("global",{}).get("hosts",{}).get("bitbucket") or "")
except: print("")' "$prefs_file" 2>/dev/null)

# Per-account overrides from prefs.global.accounts[]. Returns "" if id not found.
account_host_override() {
  local short_id="$1" field="$2"
  python3 -c '
import json, sys
short_id = sys.argv[1]
field = sys.argv[2]
try:
    accounts = json.load(open(sys.argv[3])).get("global",{}).get("accounts",[]) or []
    for a in accounts:
        if a.get("id") == short_id:
            print(a.get(field) or "")
            sys.exit(0)
    print("")
except Exception:
    print("")
' "$short_id" "$field" "$prefs_file" 2>/dev/null
}

# 1. Pull every credential entry that looks like one of our token services.
#    Includes token entries AND username entries (BB needs the user keychain key).
#    Uses cross-platform credential-store wrapper (macOS/Windows/Linux backends),
#    located via the shared resolver (repo checkout or either install tree)  -
#    never a hardcoded path. A missing helper degrades to an empty inventory.
# A pre-set CRED_STORE (tests, custom installs) is honored as-is.
if [ -z "${CRED_STORE:-}" ]; then
  # shellcheck disable=SC1090,SC1091
  # Existence check before sourcing: `. <missing>` aborts the shell under `set -e`,
  # `||` included, so a `.`-chain reaches neither its later candidates nor its error
  # branch. The loop also covers all three hosts - the chain it replaced knew only
  # .claude and .copilot, so a Codex-only install could not resolve at all.
  for _cred_resolver in \
    "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/credential-store-resolver.sh" \
    "$HOME/.claude/lib/credential-store-resolver.sh" \
    "$HOME/.copilot/lib/credential-store-resolver.sh" \
    "$HOME/.codex/lib/credential-store-resolver.sh"; do
    [ -f "$_cred_resolver" ] || continue
    # shellcheck source=/dev/null
    . "$_cred_resolver" 2>/dev/null || true
    if [ -n "${CRED_STORE:-}" ]; then break; fi
  done
  unset _cred_resolver
fi
services=""
if [ -n "${CRED_STORE:-}" ]; then
  services=$("$CRED_STORE" list 2>/dev/null \
    | grep -E '_(Github|Jira|Bitbucket|Confluence)_[A-Za-z]+_(Token|Json|Username)$|_Github_Auth_Token$|_Github_Access_Token$|_Jira_Access_Token$|_Bitbucket_Access_Token$|_Bitbucket_Username$|_Confluence_Access_Token$' \
    || true)
fi

# 2. Compute unique prefixes (everything before the provider segment).
prefixes=$(printf '%s\n' "$services" \
  | sed -E 's/_(Github|Jira|Bitbucket|Confluence)_.+$//' \
  | sort -u)

# Escape ERE metacharacters so a prefix/provider is matched literally. Without
# this a prefix containing `.` matched any character (acme.corp cross-matched
# acmexcorp, mis-associating one account's keychain with another), and a `[`
# made grep error out - swallowed by `|| true`, so the provider silently vanished.
re_escape() {
  printf '%s' "$1" | sed 's/[][\\.^$*+?(){}|]/\\&/g'
}

# Helper: given a prefix and provider keyword, find the matching token service (or "").
find_token_for() {
  local prefix; prefix="$(re_escape "$1")"
  local provider; provider="$(re_escape "$2")"
  printf '%s\n' "$services" \
    | grep -E "^${prefix}_${provider}_.*(Token|Json|Auth)$" \
    | head -n1 || true
}

# Helper: given a prefix and provider keyword, find the username service entry (or "").
find_user_key_for() {
  local prefix; prefix="$(re_escape "$1")"
  local provider; provider="$(re_escape "$2")"
  printf '%s\n' "$services" \
    | grep -E "^${prefix}_${provider}_Username$" \
    | head -n1 || true
}

emit_accounts() {
  local first=1
  printf '['
  while IFS= read -r prefix; do
    [ -z "$prefix" ] && continue

    local gh_token jira_token bb_token conf_token bb_user_key
    gh_token=$(find_token_for "$prefix" "Github")
    jira_token=$(find_token_for "$prefix" "Jira")
    bb_token=$(find_token_for "$prefix" "Bitbucket")
    conf_token=$(find_token_for "$prefix" "Confluence")
    bb_user_key=$(find_user_key_for "$prefix" "Bitbucket")

    # Provider filter
    if [ -n "$FILTER" ]; then
      local match=0
      case "$FILTER" in
        gh|github)    [ -n "$gh_token" ] && match=1 ;;
        jira)         [ -n "$jira_token" ] && match=1 ;;
        bb|bitbucket) [ -n "$bb_token" ] && match=1 ;;
        confluence)   [ -n "$conf_token" ] && match=1 ;;
      esac
      [ $match -eq 0 ] && continue
    fi

    # Work-account heuristic: bitbucket+jira together indicates corporate.
    local is_work="false"
    if [ -n "$bb_token" ] && [ -n "$jira_token" ]; then
      is_work="true"
    fi

    # Short id (lower, dashes)  -  used for per-account override lookup.
    local short_id
    short_id=$(printf '%s' "$prefix" | tr '[:upper:]_' '[:lower:]-')

    # Hosts: per-account override (prefs.global.accounts[].jiraHost) wins; falls
    # back to global.hosts.{jira,bitbucket}; otherwise null. No hardcoded values.
    local jira_host="" bb_host=""
    local per_jira per_bb
    per_jira=$(account_host_override "$short_id" "jiraHost")
    per_bb=$(account_host_override "$short_id" "bitbucketHost")
    if [ -n "$jira_token" ]; then
      if   [ -n "$per_jira" ];        then jira_host="$per_jira"
      elif [ -n "$prefs_jira_host" ]; then jira_host="$prefs_jira_host"
      fi
    fi
    if [ -n "$bb_token" ]; then
      if   [ -n "$per_bb" ];        then bb_host="$per_bb"
      elif [ -n "$prefs_bb_host" ]; then bb_host="$prefs_bb_host"
      fi
    fi

    # GitHub username  -  heuristically match to gh CLI accounts
    local gh_user=""
    if [ -n "$gh_token" ] && command -v gh >/dev/null 2>&1; then
      local gh_accounts
      gh_accounts=$(gh auth status 2>&1 | grep -E 'Logged in to github.com account' | sed 's/.*account //; s/ .*//' || true)
      local lower_prefix
      lower_prefix=$(printf '%s' "$prefix" | tr '[:upper:]_' '[:lower:]-')
      while IFS= read -r u; do
        [ -z "$u" ] && continue
        local lower_u
        lower_u=$(printf '%s' "$u" | tr '[:upper:]' '[:lower:]')
        # exact-match first segment
        case "$lower_u" in
          ${lower_prefix}|${lower_prefix}_*|${lower_prefix}-*) gh_user="$u"; break ;;
        esac
      done <<< "$gh_accounts"
      # second pass: substring match
      if [ -z "$gh_user" ]; then
        while IFS= read -r u; do
          [ -z "$u" ] && continue
          local lower_u
          lower_u=$(printf '%s' "$u" | tr '[:upper:]' '[:lower:]')
          case "$lower_u" in
            *${lower_prefix}*) gh_user="$u"; break ;;
          esac
        done <<< "$gh_accounts"
      fi
    fi

    local label="$prefix"
    [ "$is_work" = "true" ] && label="$prefix (work)"
    [ -n "$gh_user" ] && label="$label · gh:$gh_user"

    [ $first -eq 0 ] && printf ','
    first=0

    # Build the entry with python3 from env vars  -  printf-interpolated JSON
    # has zero escaping, so a quote or backslash in a keychain service name
    # would corrupt the whole array. Values cross into python via the
    # environment, never via string interpolation.
    ACC_ID="$short_id" ACC_LABEL="$label" ACC_PREFIX="$prefix" \
    ACC_GH_TOKEN="$gh_token" ACC_JIRA_TOKEN="$jira_token" \
    ACC_BB_TOKEN="$bb_token" ACC_CONF_TOKEN="$conf_token" \
    ACC_BB_USER_KEY="$bb_user_key" ACC_GH_USER="$gh_user" \
    ACC_JIRA_HOST="$jira_host" ACC_BB_HOST="$bb_host" ACC_IS_WORK="$is_work" \
    python3 - <<'PY'
import json, os

def env(name):
    return os.environ.get(name, "")

providers = []
if env("ACC_GH_TOKEN"):   providers.append("github")
if env("ACC_JIRA_TOKEN"): providers.append("jira")
if env("ACC_BB_TOKEN"):   providers.append("bitbucket")
if env("ACC_CONF_TOKEN"): providers.append("confluence")

print(json.dumps({
    "id":        env("ACC_ID"),
    "label":     env("ACC_LABEL"),
    "prefix":    env("ACC_PREFIX"),
    "providers": providers,
    "tokens": {
        "github":     env("ACC_GH_TOKEN"),
        "jira":       env("ACC_JIRA_TOKEN"),
        "bitbucket":  env("ACC_BB_TOKEN"),
        "confluence": env("ACC_CONF_TOKEN"),
    },
    "userKeys": {"bitbucket": env("ACC_BB_USER_KEY")},
    "users":    {"github": env("ACC_GH_USER")},
    "jiraHost":      env("ACC_JIRA_HOST") or None,
    "bitbucketHost": env("ACC_BB_HOST") or None,
    "isWork":        env("ACC_IS_WORK") == "true",
}, ensure_ascii=False), end="")
PY
  done <<< "$prefixes"
  printf ']'
}

raw=$(emit_accounts)

if [ $PRETTY -eq 1 ]; then
  printf '%s' "$raw" | python3 -m json.tool
else
  printf '%s\n' "$raw"
fi
