#!/bin/bash
# DID Visibility Check Script
# @author Matthew Raymer
#
# This script checks visibility permissions for DIDs within the endorser.ch system.
# It creates a signed JWT using admin credentials and queries the visibility API.
#
# Features:
# - JWT creation and signing using ES256K-R
# - DID visibility checking
# - Environment variable support
# - Debug logging
# - Command line argument parsing
#
# Usage:
#   ./dids_seen.sh [-d did_to_check]
#   DEBUG=1 ./dids_seen.sh  # For debug output
#
# Environment Variables:
#   ADMIN_DID          - Admin DID for authorization
#   ADMIN_PRIVATE_KEY  - Private key for signing
#   ENDORSER_API_URL   - API endpoint (defaults to test)
#   DEBUG              - Enable debug logging when set to 1

# Enhanced debug logging
debug_log() {
    if [ "${DEBUG:-0}" = "1" ]; then
        echo "DEBUG: $*" >&2
    fi
}

# Parse command line arguments
# -d: Specific DID to check visibility for
CHECK_DID=""
while getopts "d:" opt; do
    case $opt in
        d) CHECK_DID="$OPTARG" ;;
        \?) echo "Usage: $0 [-d did_to_check]" >&2; exit 1 ;;
    esac
done

# Load environment variables from .env file if present
# Supports:
# - ADMIN_DID
# - ADMIN_PRIVATE_KEY
# - ENDORSER_API_URL
if [ -f .env ]; then
    export $(cat .env | grep -v '^#' | xargs)
fi

# Default values for required parameters
ADMIN_DID=${ADMIN_DID:-"did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F"}
ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY:-"2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b"}
API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/report/whichDidsICanSee"}

# Create JWT payload with:
# - Issuer (iss)
# - Subject (sub)
# - Issued At (iat)
# - Expiration (exp)
now=$(date +%s)
exp=$((now + 86400))  # 24 hours from now

payload=$(jq -n \
    --arg iss "$ADMIN_DID" \
    --arg sub "$ADMIN_DID" \
    --arg iat "$now" \
    --arg exp "$exp" \
    '{
        iss: $iss,
        sub: $sub,
        iat: ($iat | tonumber),
        exp: ($exp | tonumber)
           }')

# Base64url encode header and payload
# Header specifies ES256K-R algorithm for Ethereum compatibility
header='{"alg":"ES256K-R","typ":"JWT"}'
header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
payload_b64=$(echo -n "$payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=')

# Create message to sign (header.payload)
message="$header_b64.$payload_b64"

# Add debug points
debug_log "Creating JWT with:"
debug_log "ADMIN_DID: $ADMIN_DID"
debug_log "API_URL: $API_URL"
debug_log "Payload: $payload"
debug_log "Header base64: $header_b64"
debug_log "Payload base64: $payload_b64"
debug_log "Message to sign: $message"

# Create temporary directory for key operations
# Uses trap to ensure cleanup on exit
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

# Create private key PEM file
# Converts raw private key to PEM format for OpenSSL
echo "-----BEGIN EC PRIVATE KEY-----" > "$TMPDIR/private.pem"
(
    echo "302e0201010420"  # Private key header
    echo -n "$ADMIN_PRIVATE_KEY"  # Private key bytes
    echo "a00706052b8104000a"  # secp256k1 OID
) | xxd -r -p | base64 >> "$TMPDIR/private.pem"
echo "-----END EC PRIVATE KEY-----" >> "$TMPDIR/private.pem"

# Write message to file for signing
echo -n "$message" > "$TMPDIR/message.txt"

# Sign message and get DER format signature
openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" > "$TMPDIR/signature.der"

# Convert DER signature to hex
der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der")

# Parse DER structure
# Extracts R and S values from signature
if [[ $der_sig =~ ^30([0-9a-f]{2})02([0-9a-f]{2})([0-9a-f]*)02([0-9a-f]{2})([0-9a-f]*)$ ]]; then
    r_len="${BASH_REMATCH[2]}"
    r_val="${BASH_REMATCH[3]}"
    s_len="${BASH_REMATCH[4]}"
    s_val="${BASH_REMATCH[5]}"
    
    debug_log "Raw signature values:"
    debug_log "  R (${#r_val} chars): $r_val"
    debug_log "  S (${#s_val} chars): $s_val"
    
    # Convert lengths to decimal
    r_len_dec=$((16#$r_len))
    s_len_dec=$((16#$s_len))
    
    # Handle R value padding
    if [ $r_len_dec -gt 32 ]; then
        r_val=${r_val: -64}  # Take last 32 bytes
    elif [ $r_len_dec -lt 32 ]; then
        r_val=$(printf "%064s" "$r_val" | tr ' ' '0')  # Left pad
    fi
    
    # Handle S value padding
    if [ $s_len_dec -gt 32 ]; then
        s_val=${s_val: -64}  # Take last 32 bytes
    elif [ $s_len_dec -lt 32 ]; then
        s_val=$(printf "%064s" "$s_val" | tr ' ' '0')  # Left pad
    fi
    
    # Ensure both values are exactly 64 characters
    r_val=$(printf "%064s" "$r_val" | tr ' ' '0')
    s_val=$(printf "%064s" "$s_val" | tr ' ' '0')
    
    debug_log "Normalized values:"
    debug_log "  R (${#r_val} chars): $r_val"
    debug_log "  S (${#s_val} chars): $s_val"
    
    # Create final signature
    concat_sig="${r_val}${s_val}"
    
    # Debug the DER parsing
    debug_log "DER Signature Analysis:"
    debug_log "  Full DER: $der_sig"
    debug_log "  Sequence length: ${BASH_REMATCH[1]}"
    debug_log "  R length: $r_len ($r_len_dec bytes)"
    debug_log "  S length: $s_len ($s_len_dec bytes)"
    
    # Debug signature components
    debug_log "Signature components:"
    debug_log "  R value: $r_val (length: ${#r_val})"
    debug_log "  S value: $s_val (length: ${#s_val})"
    debug_log "  Concatenated: $concat_sig (length: ${#concat_sig})"
    
    # Try both normal and high-S value signatures
    s_val_alt=""
    if [ $s_len_dec -gt 32 ]; then
        # Store alternative S value
        s_val_alt="$s_val"
        # Calculate N - s for high-S values
        n="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
        # Use Python for hex math to avoid bc issues
        normalized_s=$(python3 -c "
            n = int('$n', 16)
            s = int('${s_val}', 16)
            result = hex(n - s)[2:].zfill(64)
            print(result.lower())
        " 2>/dev/null || echo "ERROR")
        if [ "$normalized_s" = "ERROR" ]; then
            debug_log "  Failed to normalize S value"
            # Keep original s_val if normalization fails
            s_val_alt=""
        else
            s_val=$(printf "%064s" "$normalized_s" | tr ' ' '0' | tr '[:upper:]' '[:lower:]')
            debug_log "  Normalized S value: $s_val"
        fi
    fi
    
    # Calculate recovery bit (v)
    # Try each possible recovery value (0-3) until we find one that works
    for v in {0..3}; do
        # Try both S values if we have an alternative
        s_values=("$s_val")
        if [ -n "$s_val_alt" ]; then
            s_values+=("$s_val_alt")
        fi
        
        for current_s in "${s_values[@]}"; do
            concat_sig="${r_val}${current_s}"
            debug_log "Trying with S value: $current_s"
            
            recovery_sig=$(printf "%s%02x" "$concat_sig" "$v")
            debug_log "Recovery attempt $v:"
            debug_log "  Concatenated signature: $concat_sig"
            debug_log "  Recovery signature: $recovery_sig"
            debug_log "  Recovery signature length: ${#recovery_sig}"
            
            # Ensure signature is exactly 65 bytes (130 hex chars)
            if [ ${#recovery_sig} -ne 130 ]; then
                debug_log "  Invalid signature length: ${#recovery_sig}, expected 130"
                continue
            fi
            
            # Convert hex to binary, then to base64url
            signature=$(echo -n "$recovery_sig" | xxd -r -p 2>/dev/null | base64 -w 0 | tr '/+' '_-' | tr -d '=')
            debug_log "  Base64URL signature: $signature"
            
            # Create JWT with this signature
            JWT="$message.$signature"
            debug_log "  Testing JWT: $JWT"
            
            # Test the JWT against the API
            response=$(curl -s -X GET "$API_URL" \
                 -H "Content-Type: application/json" \
                 -H "Authorization: Bearer $JWT")
            debug_log "  API Response: $response"
            
            # Check if the JWT worked
            if ! echo "$response" | grep -q "JWT.*failed"; then
                echo "JWT Token:"
                echo "$JWT"
                debug_log "Success with v=$v and S=${current_s:0:8}..."
                echo
                echo "Export command:"
                echo "export TOKEN='$JWT'"
                echo
                if [ -n "$CHECK_DID" ]; then
                    # Check if specific DID is in the list
                    if echo "$response" | jq -e --arg did "$CHECK_DID" 'contains([$did])' > /dev/null; then
                        echo "✅ DID $CHECK_DID is in the list"
                        exit 0
                    else
                        echo "❌ DID $CHECK_DID is not in the list"
                        echo "Attempting to add visibility..."
                        
                        # Request visibility
                        visibility_response=$(curl -s -X POST \
                            'http://test-api.endorser.ch/api/report/canSeeMe' \
                            -H "Authorization: Bearer $JWT" \
                            -H "Content-Type: application/json" \
                            -d "{\"did\": \"$CHECK_DID\"}")
                        
                        if echo "$visibility_response" | grep -q "error"; then
                            echo "❌ Failed to add visibility:"
                            echo "$visibility_response" | jq '.' --indent 2
                            exit 1
                        else
                            echo "✅ Successfully requested visibility. Please try checking again."
                            exit 0
                        fi
                    fi
                else
                    # Show full list of visible DIDs
                    echo "$response" | jq '.' --indent 2
                fi
                exit 0
            fi
        done
    done
    
    echo "Error: Could not find valid recovery bit"
    exit 1
else
    echo "Error: Invalid DER signature format"
    echo "DER: $der_sig"
    exit 1
fi