#!/bin/bash

set -uo pipefail

# Kullanım (interaktif):  ./keychain-save.sh <service-adı>
# Örnek:                  ./keychain-save.sh github-pat
#                         ./keychain-save.sh firebase-sa
#
# HEADLESS: terminal yokken script SORMAZ. Üç `read` koruma olmadan duruyordu
# ve bir launchd ajanının altında ilki sonsuza kadar bekler; kurulum "asılı
# kaldı" diye görünür.
#
#   MULTI_AGENT_UNATTENDED=1               terminal olsa bile sorulmaz
#   ./keychain-save.sh <ad> --stdin        token stdin'den okunur (satır değil,
#                                          tüm gövde - JSON çok satırlıdır)
#   ./keychain-save.sh <ad> --json <yol>   JSON dosyadan okunur
#
# Secret İÇİN env değişkeni bilerek YOK. Bir env değeri çocuk süreçlere
# miras kalır ve bazı sistemlerde `ps e` ile görünür; borudan okumak onu
# yalnız bu sürecin belleğinde tutar. Aynı sebeple argv'ye de hiç değmez.

# Every line a person reads resolves through the table below, so this script
# answers to the same language preference the rest of the pipeline answers to.
#
# The resolver is located the same way the credential store is located further
# down - the file is checked for before it is sourced, because a `.` of a path
# that is not there takes the shell with it and the later candidates are never
# reached.
for _ma_uf in \
  "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/user-facing.sh" \
  "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../lib" 2>/dev/null && pwd)/user-facing.sh" \
  "$HOME/.claude/lib/user-facing.sh" \
  "$HOME/.copilot/lib/user-facing.sh" \
  "$HOME/.codex/lib/user-facing.sh"; do
  [ -f "$_ma_uf" ] || continue
  # shellcheck source=/dev/null
  . "$_ma_uf" 2>/dev/null || true
  if command -v ma_output_lang >/dev/null 2>&1; then break; fi
done
unset _ma_uf
if ! command -v ma_output_lang >/dev/null 2>&1; then
  echo "Error: user-facing.sh not found. Install: npx @mmerterden/multi-agent-pipeline install" >&2
  exit 1
fi
OUT_LANG=$(ma_output_lang)

# One table, both languages on the same line, so a wording change cannot land
# on one of them. English is the default and the fallback.
msg() { # $1 = key, $2.. = printf arguments
    local key="$1" en tr fmt
    shift
    case "$key" in
        usage)
            en='Usage: %s <service-name>                (interactive)\n       %s <service-name> --stdin        (token from a pipe)\n       %s <service-name> --json <path>  (token from a JSON file)'
            tr='Kullanım: %s <service-adı>                  (interaktif)\n          %s <service-adı> --stdin          (token borudan)\n          %s <service-adı> --json <yol>     (JSON dosyadan)' ;;
        no-mode-unattended)
            en='Error: MULTI_AGENT_UNATTENDED=1 and no mode was given.'
            tr='Hata: MULTI_AGENT_UNATTENDED=1 ve mod verilmedi.' ;;
        no-mode-headless)
            en='Error: there is no terminal and no mode was given.'
            tr='Hata: terminal yok ve mod verilmedi.' ;;
        file-missing)
            en='Error: file not found: %s'
            tr='Hata: Dosya bulunamadı: %s' ;;
        stdin-empty)
            en='Error: stdin is empty.'
            tr='Hata: stdin boş.' ;;
        json-needs-path)
            en='Error: --json wants a file path.'
            tr='Hata: --json bir dosya yolu ister.' ;;
        file-unreadable)
            en='Error: the file could not be read.'
            tr='Hata: Dosya okunamadı.' ;;
        kind-question)
            en='What kind of secret are you saving?'
            tr='Ne tür bir secret kaydedeceksin?' ;;
        kind-token)
            en='  1) Personal Access Token / API Key'
            tr='  1) Personal Access Token / API Key' ;;
        kind-json)
            en='  2) JSON file (service account and the like)'
            tr='  2) JSON dosyası (service account vb.)' ;;
        ask-kind)
            en='Choice (1/2): '
            tr='Seçim (1/2): ' ;;
        ask-token)
            en='Paste the token (it is typed hidden): '
            tr="Token'ı yapıştır (gizli yazılır): " ;;
        token-empty)
            en='Error: the token cannot be empty.'
            tr='Hata: Token boş olamaz.' ;;
        ask-json-path)
            en='Enter the JSON file path: '
            tr='JSON dosya yolunu gir: ' ;;
        json-stored-verbatim)
            en='The JSON will be stored as it is.'
            tr='JSON olduğu gibi saklanacak.' ;;
        invalid-choice)
            en='Invalid choice.'
            tr='Geçersiz seçim.' ;;
        unknown-mode)
            en='Error: unknown mode: %s'
            tr='Hata: bilinmeyen mod: %s' ;;
        no-credential-store)
            en='Error: credential-store not found. Install: npx @mmerterden/multi-agent-pipeline install'
            tr='Hata: credential-store bulunamadi. Kurulum: npx @mmerterden/multi-agent-pipeline install' ;;
        saved)
            en='Saved: %s (platform: %s)'
            tr='Kaydedildi: %s (platform: %s)' ;;
        read-it-back)
            en='To read it back:'
            tr='Okumak icin:' ;;
        save-failed)
            en='Error: could not save.'
            tr='Hata: Kaydedilemedi.' ;;
        *)
            printf 'keychain-save: no message for %s\n' "$key" >&2
            return 1 ;;
    esac
    [ "$OUT_LANG" = tr ] && fmt="$tr" || fmt="$en"
    # shellcheck disable=SC2059
    printf "$fmt\n" "$@"
}

SERVICE_NAME="${1:-}"
MODE="${2:-}"
MODE_ARG="${3:-}"

usage() {
    msg usage "$0" "$0" "$0"
}

if [ -z "$SERVICE_NAME" ]; then
    usage
    exit 1
fi

# Başsızken mod verilmediyse, soruyu sormak yerine reddet. Sessizce beklemek
# en kötü seçenek: dışarı hiçbir şey yazmaz ve kimse neyi beklediğini bilmez.
if [ -z "$MODE" ] && { [ ! -t 0 ] || [ "${MULTI_AGENT_UNATTENDED:-}" = "1" ]; }; then
    # Sebebi doğru söyle. "terminal yok" derken terminalin VAR olması,
    # okuyanı kendi kurulumunu yanlış yerde aramaya gönderir.
    if [ "${MULTI_AGENT_UNATTENDED:-}" = "1" ]; then
        msg no-mode-unattended >&2
    else
        msg no-mode-headless >&2
    fi
    usage >&2
    exit 1
fi

read_json_file() {
    local path="$1"
    # Kullanıcının girdiği tırnak işaretlerini temizle
    path="${path//\'/}"
    path="${path//\"/}"
    # Tilde (~) expand
    path="${path/#\~/$HOME}"
    if [ ! -f "$path" ]; then
        msg file-missing "$path" >&2
        return 1
    fi
    # JSON olduğu gibi saklanır. Kimlik deposu çok satırlı değeri
    # bayt bayt geri verir; base64 sarmalı okuma tarafında çözülmeyen
    # bir katman ekliyordu.
    cat "$path"
}

case "$MODE" in
    --stdin)
        # `read` DEĞİL `cat`: `read` ilk satırda durur ve bir service-account
        # JSON'unun geri kalanını sessizce atar.
        SECRET=$(cat)
        if [ -z "$SECRET" ]; then
            msg stdin-empty >&2
            exit 1
        fi
        ;;
    --json)
        if [ -z "$MODE_ARG" ]; then
            msg json-needs-path >&2
            usage >&2
            exit 1
        fi
        SECRET=$(read_json_file "$MODE_ARG") || exit 1
        if [ -z "$SECRET" ]; then
            msg file-unreadable >&2
            exit 1
        fi
        ;;
    "")
        msg kind-question
        msg kind-token
        msg kind-json
        # EOF (Ctrl-D) bir cevaptır: korumasız `read` onu boş bir seçime
        # çevirip aşağıdaki "Geçersiz seçim"e düşürür, ki doğru sonuç.
        read -rp "$(msg ask-kind)" CHOICE || CHOICE=""

        case "$CHOICE" in
            1)
                read -rsp "$(msg ask-token)" SECRET || SECRET=""
                echo ""
                if [ -z "$SECRET" ]; then
                    msg token-empty
                    exit 1
                fi
                ;;
            2)
                read -rp "$(msg ask-json-path)" JSON_PATH || JSON_PATH=""
                SECRET=$(read_json_file "$JSON_PATH") || exit 1
                if [ -z "$SECRET" ]; then
                    msg file-unreadable
                    exit 1
                fi
                msg json-stored-verbatim
                ;;
            *)
                msg invalid-choice
                exit 1
                ;;
        esac
        ;;
    *)
        msg unknown-mode "$MODE" >&2
        usage >&2
        exit 1
        ;;
esac

# Locate the resolver with an existence check, not a `.`-chain.
#
# Sourcing a file that does not exist aborts the shell under `set -e` - `||` included -
# so `. <candidate> || . <candidate> || { error }` reaches neither its later candidates
# nor its error branch. Every fetcher used that shape starting from `$HOME/.claude/...`,
# so on a Copilot-only or Codex-only install they all died with a bare exit 1 and no
# message. Reordering does not help: whichever candidate is absent aborts at that point.
# Checking for the file before sourcing it is the only safe form.
for _cred_resolver in \
  "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/credential-store-resolver.sh" \
  "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../lib" 2>/dev/null && 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`, not `[ ... ] && break`: the latter is the loop body's last command and returns
  # 1 when CRED_STORE is still empty, which under `set -e` kills the loop on the first
  # candidate that does not resolve - the very case the loop exists to survive.
  if [ -n "${CRED_STORE:-}" ]; then break; fi
done
unset _cred_resolver
if [ -z "${CRED_STORE:-}" ]; then
  msg no-credential-store
  exit 1
fi
CRED="$CRED_STORE"

"$CRED" delete "$SERVICE_NAME" >/dev/null 2>&1 || true
# Secret goes through stdin ("set <key> -") so it never appears on argv.
if printf '%s' "$SECRET" | "$CRED" set "$SERVICE_NAME" -; then
    msg saved "$SERVICE_NAME" "$("$CRED" platform)"
    echo ""
    msg read-it-back
    echo "  $CRED get \"$SERVICE_NAME\""
else
    msg save-failed
    exit 1
fi
