#!/usr/bin/env bash
# ring-admin - Review and process ring1/ring2 join requests
# Uses hppr CLI for all server operations

set -euo pipefail

SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./_hppr-tool-resolve
source "$SCRIPT_DIR/_hppr-tool-resolve"
HPPR_BIN="$(hppr_resolve_tool HPPR_BIN hppr "$SCRIPT_DIR")" || exit 1

usage() {
    cat >&2 <<'EOF'
Usage: ring-admin <type> <command> [options]

Review and process ring1 or ring2 join requests.

Commands:
  ring-admin ring1 list [-a|--all]
  ring-admin ring1 show <name>
  ring-admin ring1 approve <name> [-m <message>] [-R <ops> <coord>]... [-e <tai>]
  ring-admin ring1 deny <name> [-m <message>]
  ring-admin ring1 watch

  ring-admin ring2 list <group> [-a|--all]
  ring-admin ring2 show <group> <vkey>
  ring-admin ring2 approve <group> <vkey> [-m <message>] [-T <tags>]
  ring-admin ring2 deny <group> <vkey> [-m <message>]
  ring-admin ring2 watch <group>

Options:
  -a, --all                  Include requests with replies
  -m, --message <text>       Reply message
  -R, --rule <ops> <coord>   Override ACL rule (ring1 only, repeatable)
  -e, --expire <tai>         Override expiry (ring1 only)
  -T, --tags <tags>          Set member tags (ring2 only)
  -r, --raw                  Show raw packet (for show command)
  --dry-run                  Show what would be done

Environment:
  HPPR_HOME                  Server address (default: tcp+127.0.0.1:4777)
  HPPR_SIGNER                    Ring0 identity (required for approve/deny)

Examples:
  ring-admin ring1 list
  ring-admin ring1 show alice
  HPPR_SIGNER='ring1:ring0|secret' ring-admin ring1 approve alice -m "Welcome!"
  HPPR_SIGNER='ring1:ring0|secret' ring-admin ring1 deny mallory -m "Invalid request"
  ring-admin ring2 list mygroup
  HPPR_SIGNER='ring1:ring0|&.key.H3' ring-admin ring2 approve mygroup V.xxx.H3 -T "developer"
EOF
}

# Format TAI as ISO8601-ish for display
format_tai() {
    local tai="$1"
    local sec="${tai%%:*}"
    # TAI is ~37 seconds ahead of UTC
    local utc=$((sec - 37))
    date -u -d "@$utc" "+%Y-%m-%dT%H:%M:%S" 2>/dev/null || echo "$tai"
}

# Get ring0's oldest signing key (for pac -k)
# Uses identity from HPPR_SIGNER, falls back to checking if key is embedded
get_ring0_key() {
    # If identity already has a key embedded, extract it
    if [[ "${HPPR_SIGNER:-}" == *"#&."* ]]; then
        echo "${HPPR_SIGNER#*#}"
        return 0
    fi
    
    # Otherwise fetch oldest key from ring0 keys
    local keys_list
    keys_list=$("$HPPR_BIN" list "//repo/admin/ring1/ring0/keys/|/seal/" 2>/dev/null) || {
        echo "ERROR: Cannot list ring0 keys" >&2
        return 1
    }
    
    # Get oldest key (first in sorted list by TAI)
    local oldest_vkey
    oldest_vkey=$(echo "$keys_list" | head -1 | tr -d '/')
    [[ -n "$oldest_vkey" ]] || { echo "ERROR: No ring0 keys found" >&2; return 1; }
    
    # Fetch secret key from key packet
    local key_headers
    key_headers=$("$HPPR_BIN" headers "//repo/admin/ring1/ring0/keys/|/seal/$oldest_vkey" 2>/dev/null) || {
        echo "ERROR: Cannot read ring0 key" >&2
        return 1
    }
    
    local secret_key
    secret_key=$(echo "$key_headers" | sed -n 's/^Secret-Key: //p')
    [[ -n "$secret_key" ]] || { echo "ERROR: No Secret-Key in ring0 key packet" >&2; return 1; }
    
    echo "$secret_key"
}

# Get reply status from reply coordinate
get_reply_status() {
    local reply_coord="$1"
    "$HPPR_BIN" headers "$reply_coord" 2>/dev/null | sed -n 's/^Request-Status: //p' || true
}

# Extract original secret from Ring1-Secret-Token format
# Format: "V.<derived>.H3 <secret>"
extract_original_secret() {
    local token="$1"
    if [[ "$token" =~ ^V\.[A-Za-z0-9_~-]{43}\.H3[[:space:]]+(.+)$ ]]; then
        echo "${BASH_REMATCH[1]}"
        return 0
    fi

    echo "ERROR: Ring1-Secret-Token must be 'V.<derived>.H3 <secret>', got: $token" >&2
    return 1
}

# ============================================================================
# Ring1 Commands
# ============================================================================

ring1_list() {
    local show_all=0

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -a|--all) show_all=1; shift ;;
            *) shift ;;
        esac
    done

    local request_base="//repo/admin/request/join/"

    # List all ring1 request directories
    local names
    names=$("$HPPR_BIN" list "$request_base" 2>/dev/null | grep -v '^|' | sed 's|/$||' || true)

    if [[ -z "$names" ]]; then
        echo "No pending requests"
        return 0
    fi

    printf "%-20s %-20s %s\n" "NAME" "SUBMITTED" "STATUS"
    printf "%-20s %-20s %s\n" "----" "---------" "------"

    echo "$names" | while IFS= read -r name; do
        [[ -n "$name" ]] || continue

        local request_coord="${request_base}${name}/|"
        local reply_coord="${request_base}${name}/reply/|"

        # Get TAI from request
        local tai
        tai=$("$HPPR_BIN" headers "$request_coord" 2>/dev/null | sed -n 's/^TAI: //p' || true)
        [[ -n "$tai" ]] || continue
        local submitted
        submitted=$(format_tai "$tai")

        # Check reply status
        local status="(pending)"
        local reply_status
        reply_status=$(get_reply_status "$reply_coord")
        if [[ -n "$reply_status" ]]; then
            status="($reply_status)"
            [[ $show_all -eq 0 ]] && continue
        fi

        printf "%-20s %-20s %s\n" "$name" "$submitted" "$status"
    done
}

ring1_show() {
    local name="" raw=0

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -r|--raw) raw=1; shift ;;
            -*) shift ;;
            *) [[ -z "$name" ]] && name="$1"; shift ;;
        esac
    done

    [[ -n "$name" ]] || { echo "ERROR: ring1 name required" >&2; exit 1; }

    local request_coord="//repo/admin/request/join/$name/|"
    local reply_coord="//repo/admin/request/join/$name/reply/|"

    if [[ $raw -eq 1 ]]; then
        "$HPPR_BIN" get "$request_coord"
        return
    fi

    # Parse and display request
    local hdrs
    hdrs=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }

    local hash tai ring1_name
    hash=$(echo "$hdrs" | head -1 | cut -d' ' -f2)
    tai=$(echo "$hdrs" | sed -n 's/^TAI: //p')
    ring1_name=$(echo "$hdrs" | sed -n 's/^Ring1-Name: //p')

    echo "Request: $hash"
    echo "Name: $ring1_name"
    echo "Token: ********"
    echo "Submitted: $(format_tai "$tai")"

    # Show rules
    local rules
    rules=$(echo "$hdrs" | grep "^Ring1-Rule: " | sed 's/^Ring1-Rule: /  /' || true)
    if [[ -n "$rules" ]]; then
        echo "Requested rules:"
        echo "$rules"
    fi

    # Show expire
    local expire
    expire=$(echo "$hdrs" | sed -n 's/^Ring1-Expire: //p')
    [[ -n "$expire" ]] && echo "Expire: $expire"

    # Show message
    local msg
    msg=$("$HPPR_BIN" data "$request_coord" 2>/dev/null || true)
    if [[ -n "$msg" ]]; then
        echo "Message:"
        printf '  %s\n' "${msg//$'\n'/$'\n  '}"
    fi

    # Show reply status
    echo ""
    local reply_status
    reply_status=$(get_reply_status "$reply_coord")
    if [[ -n "$reply_status" ]]; then
        echo "Reply: $reply_status"
        local reply_msg
        reply_msg=$("$HPPR_BIN" data "$reply_coord" 2>/dev/null || true)
        if [[ -n "$reply_msg" ]]; then
            printf '  %s\n' "${reply_msg//$'\n'/$'\n  '}"
        fi
    else
        echo "Reply: (none)"
    fi
}

ring1_approve() {
    local name="" message="" dry_run=0 expire=""
    local -a rules=()

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -m|--message) message="$2"; shift 2 ;;
            -R|--rule)
                [[ $# -ge 2 ]] || { echo "ERROR: -R requires '<ops> <coord>' or '<ops>' '<coord>'" >&2; exit 1; }
                if [[ $# -ge 3 && "$3" == //* ]]; then
                    # Two-arg form: -R rwl //u/bob/
                    rules+=("$2 $3"); shift 3
                else
                    # Single-arg form: -R 'rwl //u/bob/'
                    rules+=("$2"); shift 2
                fi ;;
            -e|--expire) expire="$2"; shift 2 ;;
            --dry-run) dry_run=1; shift ;;
            -*) echo "Unknown option: $1" >&2; exit 1 ;;
            *) [[ -z "$name" ]] && name="$1"; shift ;;
        esac
    done

    [[ -n "$name" ]] || { echo "ERROR: ring1 name required" >&2; exit 1; }

    local request_coord="//repo/admin/request/join/$name/|"

    # Get request
    local request_headers
    request_headers=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }
    local request_hash
    request_hash=$(echo "$request_headers" | head -1 | cut -d' ' -f2)

    if [[ $dry_run -eq 1 ]]; then
        echo "Would approve ring1 '$name'"
        echo "  Request: $request_hash"
        echo "  Reply to: //repo/admin/request/join/$name/reply"
        echo "  Config to: //repo/admin/ring1/$name/setup"
        return 0
    fi

    # Get signing key
    local sign_key
    sign_key=$(get_ring0_key) || exit 1

    # Write reply using pac -k
    local reply_hash
    reply_hash=$(echo "${message:-Approved}" | "$HPPR_BIN" add -k "$sign_key" \
        -H "Request-Status: approved" \
        -H "+Link: request $request_hash" \
        "//repo/admin/request/join/$name/reply")

    # Extract headers from request for ring1 creation
    local token original_secret
    token=$(echo "$request_headers" | sed -n 's/^Ring1-Secret-Token: //p')
    [[ -n "$token" ]] || { echo "ERROR: Request missing Ring1-Secret-Token" >&2; exit 1; }
    original_secret=$(extract_original_secret "$token") || exit 1

    # Create ring1 using ring1 subcommand
    "$HPPR_BIN" ring1 create "$name" "$original_secret" >/dev/null

    # Apply rules (from request or overrides)
    if [[ ${#rules[@]} -gt 0 ]]; then
        for rule in "${rules[@]}"; do
            local ops="${rule%% *}"
            local coord="${rule#* }"
            "$HPPR_BIN" ring1 acl "$name" add "$ops" "$coord" >/dev/null
        done
    else
        # Use rules from request
        echo "$request_headers" | grep "^Ring1-Rule: " | while IFS= read -r line; do
            local rule="${line#Ring1-Rule: }"
            local ops="${rule%% *}"
            local coord="${rule#* }"
            "$HPPR_BIN" ring1 acl "$name" add "$ops" "$coord" >/dev/null
        done
    fi

    # Apply expire (from request or override)
    if [[ -n "$expire" ]]; then
        "$HPPR_BIN" ring1 expire "$name" "$expire" >/dev/null
    else
        local req_expire
        req_expire=$(echo "$request_headers" | sed -n 's/^Ring1-Expire: //p')
        [[ -n "$req_expire" ]] && "$HPPR_BIN" ring1 expire "$name" "$req_expire" >/dev/null
    fi

    echo "Approved ring1 '$name'"
    echo "  Reply stored: $reply_hash"
}

ring1_deny() {
    local name="" message=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -m|--message) message="$2"; shift 2 ;;
            -*) echo "Unknown option: $1" >&2; exit 1 ;;
            *) [[ -z "$name" ]] && name="$1"; shift ;;
        esac
    done

    [[ -n "$name" ]] || { echo "ERROR: ring1 name required" >&2; exit 1; }

    local request_coord="//repo/admin/request/join/$name/|"

    # Get request hash for Link header
    local request_headers
    request_headers=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }
    local request_hash
    request_hash=$(echo "$request_headers" | head -1 | cut -d' ' -f2)

    # Get signing key
    local sign_key
    sign_key=$(get_ring0_key) || exit 1

    # Write deny reply
    local reply_hash
    reply_hash=$(echo "${message:-Denied}" | "$HPPR_BIN" add -k "$sign_key" \
        -H "Request-Status: denied" \
        -H "+Link: request $request_hash" \
        "//repo/admin/request/join/$name/reply")

    echo "Denied ring1 '$name'"
    echo "  Reply stored: $reply_hash"
}

ring1_watch() {
    local request_base="//repo/admin/request/join/"

    echo "Watching for ring1 requests..."
    "$HPPR_BIN" watch "$request_base" | while IFS= read -r event; do
        # Check for FATAL
        if [[ "$event" == FATAL* ]]; then
            echo "$event" >&2
            exit 1
        fi

        # Parse event: "+ //repo/admin/request/join/<name>/|/..."
        if [[ "$event" == "+ "*"/|/"* ]]; then
            local name
            name=$(echo "$event" | sed -n 's#.*request/join/\([^/|]*\)/|/.*#\1#p')
            [[ -n "$name" ]] || continue

            local request_coord="${request_base}${name}/|"
            local hdrs
            hdrs=$("$HPPR_BIN" headers "$request_coord" 2>/dev/null) || continue

            local tai rules_preview msg_preview
            tai=$(echo "$hdrs" | sed -n 's/^TAI: //p')
            rules_preview=$(echo "$hdrs" | grep "^Ring1-Rule: " | head -2 | sed 's/^Ring1-Rule: //' | tr '\n' ';' | sed 's/;$//')
            msg_preview=$("$HPPR_BIN" data "$request_coord" 2>/dev/null | head -c 50 || true)

            echo ""
            echo "[NEW] ring1 request from '$name' at $(format_tai "$tai")"
            [[ -n "$rules_preview" ]] && echo "      Rules: $rules_preview"
            [[ -n "$msg_preview" ]] && echo "      Message: ${msg_preview}..."
            echo ""
            echo "Action: ring-admin ring1 approve $name"
            echo "    or: ring-admin ring1 deny $name"
        fi
    done
}

# ============================================================================
# Ring2 Commands
# ============================================================================

ring2_list() {
    local group="" show_all=0

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -a|--all) show_all=1; shift ;;
            -*) shift ;;
            *) [[ -z "$group" ]] && group="$1"; shift ;;
        esac
    done

    [[ -n "$group" ]] || { echo "ERROR: group name required" >&2; exit 1; }

    local request_base="//$group/admin/request/join/"

    # Ring2 requests are at |/seal/<vkey>
    local seals
    seals=$("$HPPR_BIN" list "${request_base}|/seal/" 2>/dev/null || true)

    if [[ -z "$seals" ]]; then
        echo "No pending requests"
        return 0
    fi

    printf "%-50s %-20s %-15s %s\n" "VERIFICATION-KEY" "SUBMITTED" "TAGS" "STATUS"
    printf "%-50s %-20s %-15s %s\n" "----------------" "---------" "----" "------"

    echo "$seals" | while IFS= read -r vkey_entry; do
        [[ -n "$vkey_entry" ]] || continue
        local vkey="${vkey_entry%/}"

        local request_coord="${request_base}|/seal/$vkey"
        local reply_coord="${request_base}${vkey}/reply/|"

        # Get TAI and tags from request
        local hdrs
        hdrs=$("$HPPR_BIN" headers "$request_coord" 2>/dev/null) || continue
        local tai tags
        tai=$(echo "$hdrs" | sed -n 's/^TAI: //p')
        tags=$(echo "$hdrs" | sed -n 's/^Request-Tags: //p')
        local submitted
        submitted=$(format_tai "$tai")

        # Check reply status
        local status="(pending)"
        local reply_status
        reply_status=$(get_reply_status "$reply_coord")
        if [[ -n "$reply_status" ]]; then
            status="($reply_status)"
            [[ $show_all -eq 0 ]] && continue
        fi

        printf "%-50s %-20s %-15s %s\n" "$vkey" "$submitted" "${tags:--}" "$status"
    done
}

ring2_show() {
    local group="" vkey="" raw=0

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -r|--raw) raw=1; shift ;;
            -*) shift ;;
            *) 
                if [[ -z "$group" ]]; then group="$1"
                elif [[ -z "$vkey" ]]; then vkey="$1"
                fi
                shift ;;
        esac
    done

    [[ -n "$group" ]] || { echo "ERROR: group name required" >&2; exit 1; }
    [[ -n "$vkey" ]] || { echo "ERROR: verification-key required" >&2; exit 1; }

    local request_coord="//$group/admin/request/join/|/seal/$vkey"
    local reply_coord="//$group/admin/request/join/$vkey/reply/|"

    if [[ $raw -eq 1 ]]; then
        "$HPPR_BIN" get "$request_coord"
        return
    fi

    # Parse and display request
    local hdrs
    hdrs=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }

    local hash tai tags
    hash=$(echo "$hdrs" | head -1 | cut -d' ' -f2)
    tai=$(echo "$hdrs" | sed -n 's/^TAI: //p')
    tags=$(echo "$hdrs" | sed -n 's/^Request-Tags: //p')

    echo "Request: $hash"
    echo "Verification-Key: $vkey"
    echo "Submitted: $(format_tai "$tai")"
    [[ -n "$tags" ]] && echo "Requested Tags: $tags"

    # Show message
    local msg
    msg=$("$HPPR_BIN" data "$request_coord" 2>/dev/null || true)
    if [[ -n "$msg" ]]; then
        echo "Message:"
        printf '  %s\n' "${msg//$'\n'/$'\n  '}"
    fi

    # Show reply status
    echo ""
    local reply_status
    reply_status=$(get_reply_status "$reply_coord")
    if [[ -n "$reply_status" ]]; then
        echo "Reply: $reply_status"
        local reply_msg
        reply_msg=$("$HPPR_BIN" data "$reply_coord" 2>/dev/null || true)
        if [[ -n "$reply_msg" ]]; then
            printf '  %s\n' "${reply_msg//$'\n'/$'\n  '}"
        fi
    else
        echo "Reply: (none)"
    fi
}

ring2_approve() {
    local group="" vkey="" message="" tags="" dry_run=0

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -m|--message) message="$2"; shift 2 ;;
            -T|--tags) tags="$2"; shift 2 ;;
            --dry-run) dry_run=1; shift ;;
            -*) echo "Unknown option: $1" >&2; exit 1 ;;
            *)
                if [[ -z "$group" ]]; then group="$1"
                elif [[ -z "$vkey" ]]; then vkey="$1"
                fi
                shift ;;
        esac
    done

    [[ -n "$group" ]] || { echo "ERROR: group name required" >&2; exit 1; }
    [[ -n "$vkey" ]] || { echo "ERROR: verification-key required" >&2; exit 1; }

    local request_coord="//$group/admin/request/join/|/seal/$vkey"

    # Get request
    local request_headers
    request_headers=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }
    local request_hash
    request_hash=$(echo "$request_headers" | head -1 | cut -d' ' -f2)

    # Use requested tags if no override
    if [[ -z "$tags" ]]; then
        tags=$(echo "$request_headers" | sed -n 's/^Request-Tags: //p')
    fi

    if [[ $dry_run -eq 1 ]]; then
        echo "Would approve ring2 member '$vkey' in group '$group'"
        echo "  Request: $request_hash"
        echo "  Tags: ${tags:-(none)}"
        return 0
    fi

    # Get signing key
    local sign_key
    sign_key=$(get_ring0_key) || exit 1

    # Write reply
    local reply_hash
    reply_hash=$(echo "${message:-Approved}" | "$HPPR_BIN" add -k "$sign_key" \
        -H "Request-Status: approved" \
        -H "+Link: request $request_hash" \
        "//$group/admin/request/join/$vkey/reply")

    # Add member to membership config
    if [[ -n "$tags" ]]; then
        local -a tag_words=()
        read -r -a tag_words <<< "$tags"
        "$HPPR_BIN" ring2 members "//$group" add "$vkey" "${tag_words[@]}" >/dev/null 2>&1 || {
            echo "Warning: Could not add to membership config (may need manual add)" >&2
        }
    else
        "$HPPR_BIN" ring2 members "//$group" add "$vkey" >/dev/null 2>&1 || {
            echo "Warning: Could not add to membership config (may need manual add)" >&2
        }
    fi

    echo "Approved ring2 member '$vkey' in group '$group'"
    echo "  Tags: ${tags:-(none)}"
    echo "  Reply stored: $reply_hash"
}

ring2_deny() {
    local group="" vkey="" message=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -m|--message) message="$2"; shift 2 ;;
            -*) echo "Unknown option: $1" >&2; exit 1 ;;
            *)
                if [[ -z "$group" ]]; then group="$1"
                elif [[ -z "$vkey" ]]; then vkey="$1"
                fi
                shift ;;
        esac
    done

    [[ -n "$group" ]] || { echo "ERROR: group name required" >&2; exit 1; }
    [[ -n "$vkey" ]] || { echo "ERROR: verification-key required" >&2; exit 1; }

    local request_coord="//$group/admin/request/join/|/seal/$vkey"

    # Get request hash for Link header
    local request_headers
    request_headers=$("$HPPR_BIN" headers "$request_coord") || { echo "ERROR: Request not found" >&2; exit 1; }
    local request_hash
    request_hash=$(echo "$request_headers" | head -1 | cut -d' ' -f2)

    # Get signing key
    local sign_key
    sign_key=$(get_ring0_key) || exit 1

    # Write deny reply
    local reply_hash
    reply_hash=$(echo "${message:-Denied}" | "$HPPR_BIN" add -k "$sign_key" \
        -H "Request-Status: denied" \
        -H "+Link: request $request_hash" \
        "//$group/admin/request/join/$vkey/reply")

    echo "Denied ring2 member '$vkey' in group '$group'"
    echo "  Reply stored: $reply_hash"
}

ring2_watch() {
    local group="$1"
    [[ -n "$group" ]] || { echo "ERROR: group name required" >&2; exit 1; }

    local request_base="//$group/admin/request/join/"

    echo "Watching for ring2 requests in group '$group'..."
    "$HPPR_BIN" watch "$request_base" | while IFS= read -r event; do
        # Check for FATAL
        if [[ "$event" == FATAL* ]]; then
            echo "$event" >&2
            exit 1
        fi

        # Parse event for new seals at |/seal/<vkey>
        if [[ "$event" == "+ "*"|/seal/"* ]]; then
            local vkey
            vkey=$(echo "$event" | sed -n 's|.*|/seal/\([^/]*\).*|\1|p')
            [[ -n "$vkey" ]] || continue

            local request_coord="${request_base}|/seal/$vkey"
            local hdrs
            hdrs=$("$HPPR_BIN" headers "$request_coord" 2>/dev/null) || continue

            local tai tags msg_preview
            tai=$(echo "$hdrs" | sed -n 's/^TAI: //p')
            tags=$(echo "$hdrs" | sed -n 's/^Request-Tags: //p')
            msg_preview=$("$HPPR_BIN" data "$request_coord" 2>/dev/null | head -c 50 || true)

            echo ""
            echo "[NEW] ring2 request from '$vkey' at $(format_tai "$tai")"
            [[ -n "$tags" ]] && echo "      Tags: $tags"
            [[ -n "$msg_preview" ]] && echo "      Message: ${msg_preview}..."
            echo ""
            echo "Action: ring-admin ring2 approve $group $vkey"
            echo "    or: ring-admin ring2 deny $group $vkey"
        fi
    done
}

# ============================================================================
# Main
# ============================================================================

[[ $# -ge 1 ]] || { usage; exit 1; }

case "$1" in
    ring1)
        [[ $# -ge 2 ]] || { usage; exit 1; }
        cmd="$2"; shift 2
        case "$cmd" in
            list)    ring1_list "$@" ;;
            show)    ring1_show "$@" ;;
            approve) ring1_approve "$@" ;;
            deny)    ring1_deny "$@" ;;
            watch)   ring1_watch "$@" ;;
            *)       echo "Unknown ring1 command: $cmd" >&2; usage; exit 1 ;;
        esac
        ;;
    ring2)
        [[ $# -ge 2 ]] || { usage; exit 1; }
        cmd="$2"; shift 2
        case "$cmd" in
            list)    ring2_list "$@" ;;
            show)    ring2_show "$@" ;;
            approve) ring2_approve "$@" ;;
            deny)    ring2_deny "$@" ;;
            watch)   ring2_watch "$@" ;;
            *)       echo "Unknown ring2 command: $cmd" >&2; usage; exit 1 ;;
        esac
        ;;
    -h|--help)
        usage; exit 0
        ;;
    *)
        echo "Unknown type: $1 (use ring1 or ring2)" >&2
        usage; exit 1
        ;;
esac
