#!/usr/bin/env bash
# add_user.sh — добавить пользователя в caddy/users.caddy.snippet
# Usage: ./scripts/auth/add_user.sh <username> "<comment>"
#
# - Читает пароль из stdin (pipe) или генерирует 16-char random если интерактивен.
# - Bcrypt через `docker run --rm caddy:2 caddy hash-password`.
# - Печатает plain password на stdout ОДИН РАЗ.
# - НЕ коммитит изменения — это задача разработчика.
set -euo pipefail

# Скрипт лежит в `<repo>/scripts/auth/`; snippet — в `<repo>/caddy/`.
# Нужно 3× `dirname`: $0 (.../scripts/auth/add_user.sh) → .../scripts/auth → .../scripts → .../<repo>.
# Bug fix 2026-05-26 — было 2× dirname (см. [[Bug_AddUser_Sh_Snippet_Path_Fixed]]).
SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
SNIPPET="$REPO_ROOT/caddy/users.caddy.snippet"

usage() {
    echo "Usage: $0 <username> \"<comment>\"" >&2
    exit 1
}

# ─── args ────────────────────────────────────────────────────────────────────

[[ $# -lt 2 ]] && usage

USERNAME="$1"
COMMENT="$2"
DATE="$(date -u +%Y-%m-%d)"

# Validate username: ^[a-z][a-z0-9_.-]{1,62}$
# Allows dot/underscore (john.doe, jane_smith) and 2-char names; starts with a
# letter. 63-char cap keeps it sane for a Caddyfile basic_auth entry (#427).
if ! [[ "$USERNAME" =~ ^[a-z][a-z0-9_.-]{1,62}$ ]]; then
    echo "ERROR: invalid username '$USERNAME'. Must match ^[a-z][a-z0-9_.-]{1,62}$" >&2
    exit 1
fi

# ─── idempotency guard ────────────────────────────────────────────────────────

# Escape regex metachars (notably the now-allowed '.') so the username is matched
# literally in the existence probe below (#427).
USERNAME_RE=$(printf '%s' "$USERNAME" | sed -E 's/[.[\^$*+?(){}|]/\\&/g')

if grep -qP "^\s+${USERNAME_RE}\s" "$SNIPPET" 2>/dev/null; then
    echo "ERROR: user '$USERNAME' already exists in $SNIPPET. Remove first with remove_user.sh." >&2
    exit 1
fi

# ─── password ─────────────────────────────────────────────────────────────────

if [ -t 0 ]; then
    # Interactive — generate random password
    PLAIN_PASS="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 16)"
else
    # Piped input
    read -r PLAIN_PASS
fi

if [[ -z "$PLAIN_PASS" ]]; then
    echo "ERROR: empty password" >&2
    exit 1
fi

# ─── bcrypt via caddy container ───────────────────────────────────────────────
# Caddy 2.11+ requires base64-encoded bcrypt in Caddyfile basic_auth.
# Password passed via stdin to avoid exposure in process list.

HASH="$(printf '%s' "$PLAIN_PASS" | docker run --rm -i caddy:2 sh -c '
    pass=$(cat)
    raw=$(caddy hash-password --algorithm bcrypt --plaintext "$pass")
    printf "%s" "$raw" | base64 | tr -d "\n"
')"

if [[ -z "$HASH" ]]; then
    echo "ERROR: caddy hash-password returned empty hash" >&2
    exit 1
fi

# ─── insert into snippet ─────────────────────────────────────────────────────
# Insert before the closing brace of basic_auth block

if ! grep -q '^}' "$SNIPPET"; then
    echo "ERROR: cannot find closing brace in $SNIPPET" >&2
    exit 1
fi

# Use a temp file for safe in-place edit
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT

# Insert new user line before the last closing `}` of the basic_auth block
awk -v user="$USERNAME" -v hash="$HASH" -v comment="$COMMENT" -v date="$DATE" '
/^}/ && !done {
    printf "    %-7s %s  # %s (%s)\n", user, hash, comment, date
    done=1
}
{ print }
' "$SNIPPET" > "$TMP"

mv "$TMP" "$SNIPPET"

# ─── output ───────────────────────────────────────────────────────────────────

echo "Added user '$USERNAME' to $SNIPPET"
echo ""
echo "Plain password (save now — not stored anywhere):"
echo "$PLAIN_PASS"
echo ""
echo "Next steps:"
echo "  1. Commit caddy/users.caddy.snippet → PR → merge → GHA deploy"
echo "  2. After deploy, Caddy auto-reloads (deploy.yml does caddy reload)"
echo "  3. Send username + password to stakeholder via secure channel"
