#!/bin/bash
# svcpass — Universal Credential Rotation Tool
# The ONE tool for ALL credential rotation across the mesh.
#
# Usage:
#   svcpass rotate --service mysql        Rotate DB service connector password
#   svcpass rotate --service admin        Rotate admin dashboard login password
#   svcpass rotate --service hmac         Rotate bridge HMAC signing key
#   svcpass rotate --service vault        Rotate vault encryption key
#   svcpass rotate --service wireguard    Rotate WireGuard keypair for this node
#   svcpass rotate --service engine-db    Rotate 669427 node DB credentials (remote)
#   svcpass rotate --service all          Everything. Full house.
#   svcpass verify                        Check all credentials (report-only)
#   svcpass status                        JSON health pill
#   svcpass discover                      Find unmanaged DB users
#   svcpass register <args>               Register new DB service
#   svcpass history                       Show rotation log
#
# Pipeline per rotation:
#   GENERATE → BACKUP → CHANGE_SOURCE → UPDATE_CONSUMERS → RESTART → VERIFY → STORE → LOG
#   If VERIFY fails → automatic ROLLBACK from BACKUP
#
# Every action logged to sec_tool_runs via dbwrite for dashboard visibility.
# Passwords NEVER in stdout, NEVER in logs, NEVER in CLI history.

TOOL_NAME="svcpass"
TOOL_VERSION="3.0.0"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
IDENTITY_DIR="/opt/sysc/engine/config"
IDENTITY_FILE="$(find "$IDENTITY_DIR" -name '*.identity' -maxdepth 1 2>/dev/null | head -1)"
[ -f "$IDENTITY_FILE" ] && source "$IDENTITY_FILE"
NODE_ID="${NODE_ID:-$(hostname -s 2>/dev/null)}"

LOG_FILE="/var/log/svc/svcpass.log"
ROTATION_LOG="/var/log/svc/rotation.log"
CREDENTIAL_REGISTRY="/opt/sysc/engine/config/credential-registry.conf"
PASSWORD_STORE="/opt/apache/tools/vault/pwstore"
PASSWORD_LENGTH=40
VAULT_KEY_PATH="/dev/shm/vault.key"
VAULT_KEY_FALLBACK="/opt/sysc/engine/config/bridge.key"

ENV_FILE="/var/www/private/.env"
HMAC_KEY_FILE="/var/www/private/bridge_hmac.key"
PRIVATE_DIR="/var/www/private"

# WireGuard config for credential service (Rule 51 — dedicated tunnel)
WG_CRED_IFACE="${WG_CRED_IFACE:-wg-p3x}"
WG_CRED_CONF="/etc/wireguard/${WG_CRED_IFACE}.conf"

# 669427 node connection (via dedicated WG tunnel or mesh fallback)
ENGINE_WG_IP="${ENGINE_WG_IP:-10.69.69.6}"
ENGINE_DB_PORT="${ENGINE_DB_PORT:-3306}"

mkdir -p "$PASSWORD_STORE" "$(dirname "$LOG_FILE")" "$(dirname "$ROTATION_LOG")" 2>/dev/null
chmod 700 "$PASSWORD_STORE" 2>/dev/null

# ═══════════════════════════════════════════════════════════════════════════
# CORE HELPERS
# ═══════════════════════════════════════════════════════════════════════════

_ts() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
_log() { echo "[$(_ts)] [${NODE_ID}] $*" >> "$LOG_FILE"; }
_log_rotation() {
    mkdir -p "$(dirname "$ROTATION_LOG")" 2>/dev/null
    echo "[$(_ts)] $*" >> "$ROTATION_LOG"
}

_audit() {
    local action="$1" detail="$2" exit_code="${3:-0}"
    local dbwrite="/opt/apache/tools/dbwrite"
    if [ -x "$dbwrite" ]; then
        "$dbwrite" tool_run "$NODE_ID" "svcpass" \
            "${action}" "$exit_code" "" "" &
        disown 2>/dev/null
    fi
    _log "AUDIT action=${action} detail=${detail} exit=${exit_code}"
}

_get_vault_key() {
    if [ -f "$VAULT_KEY_PATH" ]; then
        head -c 64 "$VAULT_KEY_PATH" 2>/dev/null
    elif [ -f "$VAULT_KEY_FALLBACK" ]; then
        head -c 64 "$VAULT_KEY_FALLBACK" 2>/dev/null
    fi
}

_require_root() {
    [ "$(id -u)" -eq 0 ] || { echo "FATAL: svcpass requires root"; exit 1; }
}

# ═══════════════════════════════════════════════════════════════════════════
# PASSWORD GENERATION — cryptographically random, class-guaranteed
# ═══════════════════════════════════════════════════════════════════════════

_generate_password() {
    local length="${1:-$PASSWORD_LENGTH}"
    local pw u l d s
    pw=$(head -c 512 /dev/urandom | tr -dc 'A-Za-z0-9!@#%^_+=-' | head -c "$length")
    u=$(head -c 16 /dev/urandom | tr -dc 'A-Z' | head -c 1)
    l=$(head -c 16 /dev/urandom | tr -dc 'a-z' | head -c 1)
    d=$(head -c 16 /dev/urandom | tr -dc '0-9' | head -c 1)
    s=$(head -c 16 /dev/urandom | tr -dc '!@#%^_+=-' | head -c 1)
    echo "${u}${l}${d}${s}${pw:4}"
}

_generate_key() {
    # 256-bit hex key (for HMAC, vault, etc.)
    openssl rand -hex 32 2>/dev/null
}

_generate_bcrypt() {
    local password="$1"
    # PHP bcrypt hash for dashboard login passwords
    php -r "echo password_hash('${password}', PASSWORD_BCRYPT, ['cost' => 12]);" 2>/dev/null
}

# ═══════════════════════════════════════════════════════════════════════════
# ENCRYPTED PASSWORD STORE — AES-256-CBC via vault key
# ═══════════════════════════════════════════════════════════════════════════

_store_secret() {
    local name="$1" value="$2"
    local vk
    vk=$(_get_vault_key)
    [ -z "$vk" ] && { _log "WARN: No vault key, cannot store ${name}"; return 1; }
    echo "$value" | openssl enc -aes-256-cbc -pbkdf2 -iter 100000 \
        -pass "pass:${vk}" -out "${PASSWORD_STORE}/${name}.enc" 2>/dev/null
    chmod 600 "${PASSWORD_STORE}/${name}.enc"
    chown root:root "${PASSWORD_STORE}/${name}.enc"
}

_get_secret() {
    local name="$1"
    local vk
    vk=$(_get_vault_key)
    [ -f "${PASSWORD_STORE}/${name}.enc" ] && [ -n "$vk" ] && \
        openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 \
            -pass "pass:${vk}" -in "${PASSWORD_STORE}/${name}.enc" 2>/dev/null
}

# ═══════════════════════════════════════════════════════════════════════════
# CONSUMER FILE UPDATERS — knows .env, PHP arrays, conf files, DSNs
# ═══════════════════════════════════════════════════════════════════════════

_backup_file() {
    local file="$1"
    [ -f "$file" ] && cp -p "$file" "${file}.bak-$(date +%s)" 2>/dev/null
}

_restore_file() {
    local file="$1"
    local latest
    latest=$(ls -t "${file}.bak-"* 2>/dev/null | head -1)
    [ -n "$latest" ] && mv -f "$latest" "$file" 2>/dev/null
}

_update_env_key() {
    local file="$1" key="$2" value="$3"
    [ -f "$file" ] || return 1
    _backup_file "$file"
    local oo og om
    oo=$(stat -c '%U' "$file" 2>/dev/null); og=$(stat -c '%G' "$file" 2>/dev/null); om=$(stat -c '%a' "$file" 2>/dev/null)
    if grep -q "^${key}=" "$file"; then
        sed -i "s|^${key}=.*|${key}=${value}|" "$file"
    else
        echo "${key}=${value}" >> "$file"
    fi
    chown "${oo}:${og}" "$file" 2>/dev/null; chmod "$om" "$file" 2>/dev/null
}

_read_env_key() {
    local file="$1" key="$2"
    grep "^${key}=" "$file" 2>/dev/null | cut -d= -f2-
}

# ═══════════════════════════════════════════════════════════════════════════
# MYSQL HELPERS
# ═══════════════════════════════════════════════════════════════════════════

_mysql_root() {
    if [ -f /root/.my.cnf ]; then
        mysql --defaults-file=/root/.my.cnf "$@" 2>/dev/null
    else
        mysql "$@" 2>/dev/null
    fi
}

_mysql_change_pw() {
    local full_user="$1" new_pw="$2"
    local user="${full_user%@*}" host="${full_user#*@}"
    [ "$host" = "$user" ] && host="localhost"
    _mysql_root -e "ALTER USER '${user}'@'${host}' IDENTIFIED BY '${new_pw}'; FLUSH PRIVILEGES;" 2>&1
}

_mysql_test() {
    local full_user="$1" pw="$2" db="${3:-}"
    local user="${full_user%@*}" host="${full_user#*@}"
    [ "$host" = "$user" ] && host="localhost"
    local cmd="SELECT 1"
    if [ -n "$db" ]; then
        mysql -u "$user" -p"$pw" -h "$host" "$db" -e "$cmd" &>/dev/null
    else
        mysql -u "$user" -p"$pw" -h "$host" -e "$cmd" &>/dev/null
    fi
}

# ═══════════════════════════════════════════════════════════════════════════
# SERVICE ROTATORS — one function per credential type
# ═══════════════════════════════════════════════════════════════════════════

# --- MySQL service password (e.g. f9a2x for store DB) ---
_rotate_mysql() {
    local target="${1:-all}"
    _init_registry
    local rotated=0 failed=0

    while IFS='|' read -r service db_user consumer_file consumer_format database unit; do
        [ -z "$service" ] && continue
        [ "$target" != "all" ] && [ "$target" != "$service" ] && continue

        echo "== [mysql] ${service} =="
        local old_pw
        old_pw=$(_get_secret "mysql-${service}")
        [ -z "$old_pw" ] && old_pw=$(_read_env_key "$consumer_file" "${consumer_format#env_key=}" 2>/dev/null)

        local new_pw
        new_pw=$(_generate_password)

        echo -n "  [1] ALTER USER ${db_user}... "
        if _mysql_change_pw "$db_user" "$new_pw"; then echo "OK"; else
            echo "FAILED"; _audit "mysql_rotate_fail" "service=${service} step=alter_user" 1; failed=$((failed+1)); continue
        fi

        echo -n "  [2] Update ${consumer_file}... "
        case "$consumer_format" in
            env_key=*) _update_env_key "$consumer_file" "${consumer_format#env_key=}" "$new_pw" && echo "OK" || { echo "FAILED"; _mysql_change_pw "$db_user" "$old_pw"; failed=$((failed+1)); continue; } ;;
            *) echo "SKIP (unsupported format)" ;;
        esac

        echo -n "  [3] Restart ${unit:-none}... "
        if [ -n "$unit" ] && systemctl is-enabled "$unit" &>/dev/null; then
            systemctl restart "$unit" 2>/dev/null; sleep 2
            systemctl is-active "$unit" &>/dev/null && echo "OK" || {
                echo "FAILED — ROLLBACK"
                [ -n "$old_pw" ] && { _mysql_change_pw "$db_user" "$old_pw"; _update_env_key "$consumer_file" "${consumer_format#env_key=}" "$old_pw"; systemctl restart "$unit" 2>/dev/null; }
                _audit "mysql_rotate_rollback" "service=${service} step=restart" 1; failed=$((failed+1)); continue
            }
        else echo "SKIP"; fi

        echo -n "  [4] Verify connection... "
        if _mysql_test "$db_user" "$new_pw" "$database"; then echo "OK"; else
            echo "FAILED — ROLLBACK"
            [ -n "$old_pw" ] && { _mysql_change_pw "$db_user" "$old_pw"; _update_env_key "$consumer_file" "${consumer_format#env_key=}" "$old_pw"; [ -n "$unit" ] && systemctl restart "$unit" 2>/dev/null; }
            _audit "mysql_rotate_rollback" "service=${service} step=verify" 1; failed=$((failed+1)); continue
        fi

        echo -n "  [5] Store in vault... "
        _store_secret "mysql-${service}" "$new_pw" && echo "OK" || echo "WARN"

        _log_rotation "MYSQL service=${service} db_user=${db_user} node=${NODE_ID} result=OK"
        _audit "mysql_rotated" "service=${service} db_user=${db_user}"
        rotated=$((rotated+1)); new_pw="X"; unset new_pw
    done < <(_read_registry)

    echo ""; echo "  MySQL: rotated=${rotated} failed=${failed}"
    [ $failed -gt 0 ] && return 1 || return 0
}

# --- Admin dashboard login password ---
_rotate_admin() {
    echo "== [admin] Dashboard Admin Password =="
    local new_pw
    new_pw=$(_generate_password 24)
    local hash
    hash=$(_generate_bcrypt "$new_pw")

    if [ -z "$hash" ]; then
        echo "  FAILED: php not available for bcrypt"
        return 1
    fi

    echo -n "  [1] Updating users table... "
    local sql="UPDATE users SET password_hash = '${hash}', password_changed_at = NOW() WHERE admin_level > 0 LIMIT 1"
    if _mysql_root -e "$sql" "store" 2>/dev/null; then echo "OK"; else
        echo "FAILED"; _audit "admin_rotate_fail" "step=db_update" 1; return 1
    fi

    echo -n "  [2] Store in vault... "
    _store_secret "admin-dashboard" "$new_pw" && echo "OK" || echo "WARN"

    echo "  [3] New admin password generated and stored in vault."
    echo "      Retrieve with: svcpass show admin"
    echo "      This password auto-expires from vault in 24h."

    _log_rotation "ADMIN node=${NODE_ID} result=OK"
    _audit "admin_rotated" "target=dashboard_admin"
    new_pw="X"; unset new_pw
}

# --- Bridge HMAC signing key ---
_rotate_hmac() {
    echo "== [hmac] Bridge HMAC Key =="
    local old_key=""
    [ -f "$HMAC_KEY_FILE" ] && old_key=$(cat "$HMAC_KEY_FILE" 2>/dev/null)

    local new_key
    new_key=$(_generate_key)

    echo -n "  [1] Write new HMAC key... "
    _backup_file "$HMAC_KEY_FILE"
    echo -n "$new_key" > "$HMAC_KEY_FILE"
    chmod 600 "$HMAC_KEY_FILE"; chown root:root "$HMAC_KEY_FILE"
    echo "OK"

    echo -n "  [2] Update .env bridge_hmac... "
    _update_env_key "$ENV_FILE" "bridge_hmac" "$new_key" && echo "OK" || echo "WARN"

    echo -n "  [3] Store old key for 669427 sync... "
    _store_secret "hmac-previous" "$old_key" && echo "OK" || echo "WARN"
    _store_secret "hmac-current" "$new_key" && echo "OK" || echo "WARN"

    echo "  [!] 669427 node HMAC must be updated separately."
    echo "      Push new key via: scp $HMAC_KEY_FILE admin@<engine>:/path/"
    echo "      Or trigger: s3rv1c3agent vault_fetch on 669427"

    _log_rotation "HMAC node=${NODE_ID} result=OK"
    _audit "hmac_rotated" "target=bridge_hmac"
    new_key="X"; unset new_key
}

# --- Vault encryption key ---
_rotate_vault_key() {
    echo "== [vault] Vault Encryption Key =="
    local old_key=""
    [ -f "$VAULT_KEY_FALLBACK" ] && old_key=$(head -c 64 "$VAULT_KEY_FALLBACK" 2>/dev/null)

    local new_key
    new_key=$(_generate_key)

    echo -n "  [1] Re-encrypt password store with new key... "
    local tmp_dir
    tmp_dir=$(mktemp -d)
    local count=0
    for enc_file in "$PASSWORD_STORE"/*.enc; do
        [ -f "$enc_file" ] || continue
        local name
        name=$(basename "$enc_file" .enc)
        local plaintext
        plaintext=$(_get_secret "$name")
        if [ -n "$plaintext" ]; then
            echo "$plaintext" | openssl enc -aes-256-cbc -pbkdf2 -iter 100000 \
                -pass "pass:${new_key}" -out "${tmp_dir}/${name}.enc" 2>/dev/null
            count=$((count+1))
        fi
    done
    echo "${count} secrets re-encrypted"

    echo -n "  [2] Install new vault key... "
    _backup_file "$VAULT_KEY_FALLBACK"
    echo -n "$new_key" > "$VAULT_KEY_FALLBACK"
    chmod 600 "$VAULT_KEY_FALLBACK"; chown root:root "$VAULT_KEY_FALLBACK"
    # Also update RAM cache
    echo -n "$new_key" > "$VAULT_KEY_PATH" 2>/dev/null
    chmod 600 "$VAULT_KEY_PATH" 2>/dev/null
    echo "OK"

    echo -n "  [3] Swap re-encrypted store... "
    for f in "$tmp_dir"/*.enc; do
        [ -f "$f" ] && mv -f "$f" "$PASSWORD_STORE/"
    done
    rm -rf "$tmp_dir"
    chmod 600 "$PASSWORD_STORE"/*.enc 2>/dev/null
    echo "OK"

    echo -n "  [4] Verify decryption with new key... "
    local test_val
    test_val=$(_get_secret "hmac-current" 2>/dev/null)
    [ -n "$test_val" ] && echo "OK" || echo "WARN (no secrets to verify)"

    _log_rotation "VAULT_KEY node=${NODE_ID} re_encrypted=${count} result=OK"
    _audit "vault_key_rotated" "re_encrypted=${count}"
    new_key="X"; unset new_key
}

# --- WireGuard keypair ---
_rotate_wireguard() {
    echo "== [wireguard] WireGuard Key Rotation =="

    local wg_ifaces
    wg_ifaces=$(wg show interfaces 2>/dev/null)
    [ -z "$wg_ifaces" ] && { echo "  No WireGuard interfaces found"; return 1; }

    for iface in $wg_ifaces; do
        echo "  -- Interface: ${iface} --"

        echo -n "  [1] Save peer config... "
        local peers_backup
        peers_backup=$(wg showconf "$iface" 2>/dev/null | grep -A5 '^\[Peer\]')
        echo "OK ($(echo "$peers_backup" | grep -c '^\[Peer\]') peers)"

        echo -n "  [2] Generate new keypair... "
        local new_privkey new_pubkey
        new_privkey=$(wg genkey 2>/dev/null)
        new_pubkey=$(echo "$new_privkey" | wg pubkey 2>/dev/null)
        echo "OK"

        echo -n "  [3] Apply new private key... "
        local conf_file="/etc/wireguard/${iface}.conf"
        if [ -f "$conf_file" ]; then
            _backup_file "$conf_file"
            sed -i "s|^PrivateKey\s*=.*|PrivateKey = ${new_privkey}|" "$conf_file"
            echo "OK"
        else
            echo "SKIP (no conf file)"
        fi

        echo -n "  [4] Restart interface... "
        wg syncconf "$iface" <(wg-quick strip "$iface" 2>/dev/null) 2>/dev/null && echo "OK" || {
            wg-quick down "$iface" 2>/dev/null; wg-quick up "$iface" 2>/dev/null && echo "OK (full restart)" || echo "FAILED"
        }

        echo "  [5] New public key: ${new_pubkey}"
        echo "      Update peers on other nodes with this public key."

        _store_secret "wg-${iface}-pubkey" "$new_pubkey"
        _log_rotation "WIREGUARD iface=${iface} node=${NODE_ID} new_pubkey=${new_pubkey:0:12}..."
        _audit "wireguard_rotated" "iface=${iface}"
        new_privkey="X"; unset new_privkey
    done
}

# --- 669427 node DB credentials (remote rotation via WireGuard) ---
_rotate_engine_db() {
    echo "== [engine-db] 669427 Node DB Credentials =="
    echo "  Target: ${ENGINE_WG_IP}:${ENGINE_DB_PORT}"

    local new_pw
    new_pw=$(_generate_password)

    echo -n "  [1] Change 669427 DB password via mesh... "
    if mysql -h "$ENGINE_WG_IP" -P "$ENGINE_DB_PORT" --defaults-file=/root/.my.cnf \
        -e "ALTER USER 'engine_svc'@'%' IDENTIFIED BY '${new_pw}'; FLUSH PRIVILEGES;" 2>/dev/null; then
        echo "OK"
    else
        echo "FAILED (check mesh connectivity)"
        return 1
    fi

    echo -n "  [2] Store in vault... "
    _store_secret "engine-db" "$new_pw" && echo "OK" || echo "WARN"

    echo "  [3] 669427 node dbwrite will fetch new creds on next vault_fetch."

    _log_rotation "ENGINE_DB node=${NODE_ID} target=${ENGINE_WG_IP} result=OK"
    _audit "engine_db_rotated" "target=${ENGINE_WG_IP}"
    new_pw="X"; unset new_pw
}

# --- .env encryption key (sk — the AES key for credentials.enc) ---
_rotate_env_sk() {
    echo "== [sk] .env Encryption Key (credentials.enc) =="

    local old_sk
    old_sk=$(_read_env_key "$ENV_FILE" "sk")
    [ -z "$old_sk" ] && { echo "  FAILED: No existing sk in .env"; return 1; }

    local new_sk
    new_sk=$(openssl rand -hex 32 2>/dev/null)

    echo -n "  [1] Re-encrypt credentials.enc with new key... "
    local cred_file="${PRIVATE_DIR}/credentials.enc"
    if [ -f "$cred_file" ] && command -v php &>/dev/null; then
        local plaintext
        plaintext=$(php -r "
            \$sk = hex2bin('${old_sk}');
            \$raw = file_get_contents('${cred_file}');
            \$iv = substr(\$raw, 1, 12);
            \$tag = substr(\$raw, 13, 16);
            \$ct = substr(\$raw, 29);
            \$pt = openssl_decrypt(\$ct, 'aes-256-gcm', \$sk, OPENSSL_RAW_DATA, \$iv, \$tag);
            if (\$pt === false) { \$iv = substr(\$raw, 0, 16); \$tag = substr(\$raw, 16, 16); \$ct = substr(\$raw, 32); \$pt = openssl_decrypt(\$ct, 'aes-256-gcm', \$sk, OPENSSL_RAW_DATA, \$iv, \$tag); }
            echo \$pt;
        " 2>/dev/null)

        if [ -n "$plaintext" ]; then
            php -r "
                \$sk = hex2bin('${new_sk}');
                \$iv = openssl_random_pseudo_bytes(12);
                \$pt = '${plaintext}';
                \$ct = openssl_encrypt(\$pt, 'aes-256-gcm', \$sk, OPENSSL_RAW_DATA, \$iv, \$tag);
                file_put_contents('${cred_file}', chr(1) . \$iv . \$tag . \$ct);
            " 2>/dev/null && echo "OK" || { echo "FAILED"; return 1; }
        else
            echo "SKIP (could not decrypt current)"
        fi
    else
        echo "SKIP (no credentials.enc or php)"
    fi

    echo -n "  [2] Update .env sk key... "
    _update_env_key "$ENV_FILE" "sk" "$new_sk" && echo "OK" || { echo "FAILED"; return 1; }

    echo -n "  [3] Store old sk for emergency... "
    _store_secret "sk-previous" "$old_sk" && echo "OK" || echo "WARN"
    _store_secret "sk-current" "$new_sk" && echo "OK" || echo "WARN"

    _log_rotation "SK node=${NODE_ID} result=OK"
    _audit "sk_rotated" "target=credentials.enc"
    new_sk="X"; unset new_sk
}

# ═══════════════════════════════════════════════════════════════════════════
# CREDENTIAL REGISTRY — MySQL service connectors
# ═══════════════════════════════════════════════════════════════════════════

_init_registry() {
    if [ ! -f "$CREDENTIAL_REGISTRY" ]; then
        mkdir -p "$(dirname "$CREDENTIAL_REGISTRY")" 2>/dev/null
        cat > "$CREDENTIAL_REGISTRY" <<'REGISTRY'
# Credential Registry — service|db_user@host|consumer_file|consumer_format|database|systemd_unit
# consumer_format: env_key=<KEY> | php_array | conf_key=<KEY> | php_dsn
store|f9a2x@127.0.0.1|/var/www/private/.env|env_key=db_pass|store|php-fpm
REGISTRY
        chmod 600 "$CREDENTIAL_REGISTRY"
    fi
}

_read_registry() {
    grep -v '^#' "$CREDENTIAL_REGISTRY" 2>/dev/null | grep -v '^$'
}

# ═══════════════════════════════════════════════════════════════════════════
# VERIFY — report-only, no auto-heal
# ═══════════════════════════════════════════════════════════════════════════

pw_verify() {
    echo "=== Credential Health Check: ${NODE_ID} ==="
    echo "  Mode: REPORT-ONLY | Time: $(_ts)"
    echo ""
    local total_ok=0 total_fail=0

    # MySQL services
    echo "-- MySQL Services --"
    _init_registry
    while IFS='|' read -r service db_user consumer_file consumer_format database unit; do
        [ -z "$service" ] && continue
        echo -n "  ${service} (${db_user}): "
        [ ! -f "$consumer_file" ] && { echo "SKIP (no file)"; continue; }
        local pw=""
        case "$consumer_format" in
            env_key=*) pw=$(_read_env_key "$consumer_file" "${consumer_format#env_key=}") ;;
        esac
        [ -z "$pw" ] && { echo "NO PASSWORD"; total_fail=$((total_fail+1)); continue; }
        local user="${db_user%@*}" host="${db_user#*@}"
        if mysql -u "$user" -p"$pw" -h "$host" "$database" -e "SELECT 1" &>/dev/null; then
            echo "OK"; total_ok=$((total_ok+1))
        else
            echo "FAILED"; total_fail=$((total_fail+1))
        fi
    done < <(_read_registry)

    # HMAC key
    echo ""
    echo "-- HMAC Bridge Key --"
    echo -n "  bridge_hmac.key: "
    [ -f "$HMAC_KEY_FILE" ] && echo "OK ($(wc -c < "$HMAC_KEY_FILE") bytes)" || { echo "MISSING"; total_fail=$((total_fail+1)); }

    # Vault key
    echo -n "  vault.key: "
    [ -f "$VAULT_KEY_PATH" ] && echo "OK (RAM)" || { [ -f "$VAULT_KEY_FALLBACK" ] && echo "OK (disk)" || { echo "MISSING"; total_fail=$((total_fail+1)); } }

    # .env sk
    echo -n "  .env sk: "
    local sk
    sk=$(_read_env_key "$ENV_FILE" "sk" 2>/dev/null)
    [ -n "$sk" ] && [ ${#sk} -eq 64 ] && echo "OK (64 hex)" || { echo "MISSING/SHORT"; total_fail=$((total_fail+1)); }

    # WireGuard
    echo ""
    echo "-- WireGuard --"
    for iface in $(wg show interfaces 2>/dev/null); do
        echo -n "  ${iface}: "
        local peers
        peers=$(wg show "$iface" peers 2>/dev/null | wc -l)
        local handshake
        handshake=$(wg show "$iface" latest-handshakes 2>/dev/null | head -1 | awk '{print $2}')
        if [ -n "$handshake" ] && [ "$handshake" != "0" ]; then
            local age=$(( $(date +%s) - handshake ))
            echo "OK (${peers} peers, last handshake ${age}s ago)"
        else
            echo "WARN (${peers} peers, no recent handshake)"
        fi
    done

    echo ""
    echo "  TOTAL: ok=${total_ok} fail=${total_fail}"
    _audit "verify" "ok=${total_ok} fail=${total_fail}"
    [ $total_fail -gt 0 ] && return 1 || return 0
}

# ═══════════════════════════════════════════════════════════════════════════
# STATUS — JSON pill for dashboard
# ═══════════════════════════════════════════════════════════════════════════

pw_status() {
    _init_registry
    local ok=0 fail=0 total=0
    while IFS='|' read -r service db_user consumer_file consumer_format database unit; do
        [ -z "$service" ] && continue; total=$((total+1))
        local pw="" user="${db_user%@*}" host="${db_user#*@}"
        case "$consumer_format" in env_key=*) pw=$(_read_env_key "$consumer_file" "${consumer_format#env_key=}") ;; esac
        [ -n "$pw" ] && mysql -u "$user" -p"$pw" -h "$host" "$database" -e "SELECT 1" &>/dev/null && ok=$((ok+1)) || fail=$((fail+1))
    done < <(_read_registry)
    local hmac_ok="false" vault_ok="false" sk_ok="false"
    [ -f "$HMAC_KEY_FILE" ] && hmac_ok="true"
    [ -f "$VAULT_KEY_PATH" ] || [ -f "$VAULT_KEY_FALLBACK" ] && vault_ok="true"
    local sk; sk=$(_read_env_key "$ENV_FILE" "sk" 2>/dev/null); [ -n "$sk" ] && sk_ok="true"
    local status="healthy"; [ $fail -gt 0 ] && status="degraded"
    printf '{"tool":"svcpass","node":"%s","version":"%s","status":"%s","mysql_ok":%d,"mysql_fail":%d,"mysql_total":%d,"hmac":%s,"vault":%s,"sk":%s,"ts":"%s"}\n' \
        "$NODE_ID" "$TOOL_VERSION" "$status" "$ok" "$fail" "$total" "$hmac_ok" "$vault_ok" "$sk_ok" "$(_ts)"
}

# ═══════════════════════════════════════════════════════════════════════════
# SHOW — retrieve a stored secret (vault-pin protected in future)
# ═══════════════════════════════════════════════════════════════════════════

pw_show() {
    _require_root
    local name="$1"
    [ -z "$name" ] && { echo "Usage: svcpass show <secret-name>"; echo "Names: admin, mysql-store, hmac-current, sk-current, wg-*-pubkey"; return 1; }
    case "$name" in
        admin) name="admin-dashboard" ;;
    esac
    local val
    val=$(_get_secret "$name")
    [ -n "$val" ] && echo "$val" || echo "(not found)"
    _audit "show" "name=${name}"
}

# ═══════════════════════════════════════════════════════════════════════════
# HISTORY — rotation log
# ═══════════════════════════════════════════════════════════════════════════

pw_history() {
    echo "=== Rotation History ==="
    if [ -f "$ROTATION_LOG" ]; then
        tail -25 "$ROTATION_LOG" | while read -r line; do echo "  $line"; done
    else
        echo "  No rotations logged yet"
    fi
}

# ═══════════════════════════════════════════════════════════════════════════
# DISCOVER — find unmanaged DB users
# ═══════════════════════════════════════════════════════════════════════════

pw_discover() {
    _require_root
    echo "=== Unmanaged DB Users ==="
    _init_registry
    local managed=""
    while IFS='|' read -r s u f fmt d unit; do managed="${managed} ${u}"; done < <(_read_registry)
    _mysql_root -N -e "SELECT User,Host FROM mysql.user ORDER BY User;" 2>/dev/null | while IFS=$'\t' read -r user host; do
        [ -z "$user" ] && continue
        case "$user" in root|mariadb.sys|mysql|rdsadmin) continue ;; esac
        local full="${user}@${host}"
        echo "$managed" | grep -q "$full" && echo "  MANAGED   ${full}" || echo "  UNMANAGED ${full}"
    done
}

# ═══════════════════════════════════════════════════════════════════════════
# REGISTER
# ═══════════════════════════════════════════════════════════════════════════

pw_register() {
    _require_root
    local service="$1" db_user="$2" file="$3" format="$4" database="$5" unit="${6:-}"
    [ -z "$database" ] && { echo "Usage: svcpass register <service> <user@host> <file> <format> <db> [unit]"; return 1; }
    _init_registry
    sed -i "/^${service}|/d" "$CREDENTIAL_REGISTRY" 2>/dev/null
    echo "${service}|${db_user}|${file}|${format}|${database}|${unit}" >> "$CREDENTIAL_REGISTRY"
    _audit "register" "service=${service} db_user=${db_user}"
    echo "Registered: ${service}"
}

# ═══════════════════════════════════════════════════════════════════════════
# MAIN ROUTER
# ═══════════════════════════════════════════════════════════════════════════

case "${1:-help}" in
    rotate)
        _require_root
        shift
        service=""
        while [ $# -gt 0 ]; do
            case "$1" in
                --service|-s) shift; service="$1" ;;
                *) service="$1" ;;
            esac
            shift
        done
        [ -z "$service" ] && { echo "Usage: svcpass rotate --service <mysql|admin|hmac|vault|wireguard|engine-db|sk|all>"; exit 1; }

        echo "=== svcpass v${TOOL_VERSION} — Credential Rotation ==="
        echo "  Node: ${NODE_ID} | Target: ${service} | Time: $(_ts)"
        echo ""

        case "$service" in
            mysql)      _rotate_mysql "all" ;;
            mysql:*)    _rotate_mysql "${service#mysql:}" ;;
            admin)      _rotate_admin ;;
            hmac)       _rotate_hmac ;;
            vault)      _rotate_vault_key ;;
            wireguard)  _rotate_wireguard ;;
            engine-db)  _rotate_engine_db ;;
            sk)         _rotate_env_sk ;;
            all)
                echo ">>> FULL ROTATION <<<"
                echo ""
                _rotate_mysql "all"
                echo ""
                _rotate_admin
                echo ""
                _rotate_hmac
                echo ""
                _rotate_vault_key
                echo ""
                echo "  [!] WireGuard and 669427-DB skipped in 'all' — run separately (requires peer coordination)"
                ;;
            *)
                # Try as a mysql service name
                _rotate_mysql "$service"
                ;;
        esac
        ;;
    verify)     _require_root; pw_verify ;;
    status)     pw_status ;;
    show)       shift; pw_show "$@" ;;
    discover)   _require_root; pw_discover ;;
    register)   shift; pw_register "$@" ;;
    history)    pw_history ;;
    run)        _require_root; pw_verify ;;
    init)       _require_root; _init_registry; echo "Initialized credential registry" ;;
    help|*)
        cat <<HELP
svcpass v${TOOL_VERSION} — Universal Credential Rotation Tool

ROTATE:
  svcpass rotate --service mysql         All registered MySQL service passwords
  svcpass rotate --service mysql:store   Just the 'store' service
  svcpass rotate --service admin         Dashboard admin login password
  svcpass rotate --service hmac          Bridge HMAC signing key
  svcpass rotate --service vault         Vault encryption key (re-encrypts store)
  svcpass rotate --service wireguard     WireGuard keypairs (all interfaces)
  svcpass rotate --service engine-db     669427 node DB credentials (remote)
  svcpass rotate --service sk            .env encryption key + re-encrypt credentials.enc
  svcpass rotate --service all           MySQL + admin + HMAC + vault + sk

CHECK:
  svcpass verify                         Full credential health check (report-only)
  svcpass status                         JSON pill for dashboard
  svcpass discover                       Find unmanaged DB users
  svcpass history                        Recent rotation log

MANAGE:
  svcpass show <name>                    Retrieve stored secret from vault
  svcpass register <svc> <u@h> ...       Register new MySQL service
  svcpass init                           Initialize credential registry

Pipeline: GENERATE > BACKUP > CHANGE > UPDATE > RESTART > VERIFY > STORE > LOG
Rollback: Automatic on any failure. Zero downtime.
HELP
        ;;
esac
