#!/bin/bash

# Check for required commands
for cmd in openssl xxd jq curl base64; do
    if ! command -v $cmd &> /dev/null; then
        echo "Error: $cmd is not installed. Please install it first."
        echo "On Arch Linux: sudo pacman -S $cmd"
        exit 1
    fi
done

# Load environment variables
if [ -f .env ]; then
    export $(cat .env | grep -v '^#' | xargs)
fi

# Default values
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/v2/claim"}
DEBUG=${DEBUG:-0}

# Function to log debug info
debug_log() {
    if [ "$DEBUG" = "1" ]; then
        echo "$@" >&2
    fi
}

# Function to generate a new keypair
generate_keypair() {
    debug_log "Generating new keypair..."
    
    # Create a temporary directory for key operations
    TMPDIR=$(mktemp -d)
    trap 'rm -rf "$TMPDIR"' EXIT
    
    # Generate private key
    openssl ecparam -name secp256k1 -genkey -noout > "$TMPDIR/private.pem"
    
    # Extract raw private key
    PRIVATE_KEY=$(openssl ec -in "$TMPDIR/private.pem" -text -noout 2>/dev/null | \
                 grep priv -A 3 | tail -n +2 | \
                 tr -d '\n[:space:]:' | \
                 sed 's/^00//')
    
    # Generate public key (compressed format)
    PUBLIC_KEY=$(openssl ec -in "$TMPDIR/private.pem" -pubout -outform DER 2>/dev/null | \
                tail -c 65 | \
                xxd -p -c 65 | \
                sed 's/^04/02/')
    
    # Generate Ethereum address
    ADDRESS=$(echo -n "$PUBLIC_KEY" | \
             xxd -r -p | \
             openssl dgst -sha3-256 -binary | \
             tail -c 20 | \
             xxd -p)
    
    # Create DID
    DID="did:ethr:0x$ADDRESS"
    
    # Print debug info if enabled
    debug_log "Generated DID Details:"
    debug_log "----------------------"
    debug_log "DID: $DID"
    debug_log "Address: $ADDRESS"
    debug_log "Private Key: $PRIVATE_KEY"
    debug_log "Public Key: $PUBLIC_KEY"
    
    # Export variables for other functions
    export DID PRIVATE_KEY PUBLIC_KEY ADDRESS
    return 0
}

# Function to hex_to_dec
hex_to_dec() {
    printf "%d" "0x$1"
}

# Function to attempt signing with retries
attempt_signing() {
    local max_attempts=5
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        debug_log "Signing attempt $attempt of $max_attempts..."
        
        # Sign message and get DER format signature
        openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" 2>/dev/null > "$TMPDIR/signature.der"
        
        # Convert DER signature to hex
        der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der")
        
        debug_log "Debug - Full DER signature:"
        debug_log "$der_sig"
        
        # Parse DER structure with length checking
        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
            seq_len="${BASH_REMATCH[1]}"
            r_len="${BASH_REMATCH[2]}"
            r_val="${BASH_REMATCH[3]}"
            s_len="${BASH_REMATCH[4]}"
            s_val="${BASH_REMATCH[5]}"
            
            # Convert lengths to decimal
            r_len_dec=$((16#$r_len))
            s_len_dec=$((16#$s_len))
            
            debug_log "R length: $r_len_dec bytes"
            debug_log "S length: $s_len_dec bytes"
            
            # Handle R value
            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
            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
            
            # Validate final lengths
            if [ ${#r_val} -eq 64 ] && [ ${#s_val} -eq 64 ]; then
                debug_log "Valid signature found on attempt $attempt"
                return 0
            fi
        fi
        
        debug_log "Invalid signature on attempt $attempt, retrying..."
        attempt=$((attempt + 1))
    done
    
    echo "Failed to generate valid signature after $max_attempts attempts" >&2
    return 1
}

# Function to create and sign JWT
create_jwt() {
    local now=$(date +%s)
    local exp=$((now + 300))
    
    # Create header (compact JSON)
    local header='{"typ":"JWT","alg":"ES256K"}'
    
    # Create registration claim (compact JSON)
    local claim="{\"iat\":$now,\"exp\":$exp,\"sub\":\"RegisterAction\",\"vc\":{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":{\"@context\":\"https://schema.org\",\"@type\":\"RegisterAction\",\"agent\":{\"did\":\"$ADMIN_DID\"},\"participant\":{\"did\":\"$DID\"},\"object\":\"endorser.ch\"}},\"iss\":\"$ADMIN_DID\"}"
    
    # Base64url encode header and claim
    header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
    claim_b64=$(echo -n "$claim" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
    
    # Create message to sign
    message="$header_b64.$claim_b64"
    
    # Create temporary directory for key operations
    TMPDIR=$(mktemp -d)
    trap 'rm -rf "$TMPDIR"' EXIT
    
    # Create private key PEM file
    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
    echo -n "$message" > "$TMPDIR/message.txt"
    
    # Attempt signing with retries
    if attempt_signing; then
        # Create final JWT
        concat_sig="${r_val}${s_val}"
        signature=$(echo -n "$concat_sig" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=')
        JWT="$message.$signature"
        
        debug_log "Created JWT: ${JWT:0:50}..."
        export JWT
        return 0
    else
        echo "Failed to generate valid signature" >&2
        return 1
    fi
}

# Function to register DID
register_did() {
    debug_log "Attempting registration..."
    
    # Create request body
    body="{\"jwtEncoded\":\"$JWT\"}"
    
    # Send registration request
    response=$(curl -s -X POST "$API_URL" \
         -H "Content-Type: application/json" \
         -d "$body")
    
    # Check response
    if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
        debug_log "Registration successful!"
        debug_log "Response: $(echo "$response" | jq -c '.')"
        
        # Output only the JSON result
        debug_log "About to output JSON..."
        jq -n \
          --arg did "$DID" \
          --arg private_key "$PRIVATE_KEY" \
          --arg registration_id "$(echo "$response" | jq -r '.success.registrationId')" \
          --arg claim_id "$(echo "$response" | jq -r '.success.claimId')" \
          '{
            status: "success",
            did: $did,
            privateKey: $private_key,
            registrationId: $registration_id,
            claimId: $claim_id
          }'
        debug_log "JSON output complete"
        exit 0
    else
        error_msg=$(echo "$response" | jq -r '.error.message // "Unknown error"')
        debug_log "Registration failed: $error_msg"
        debug_log "Full response: $(echo "$response" | jq -c '.' || echo "$response")"
        
        # Output error as JSON
        jq -n \
          --arg error "$error_msg" \
          --arg stage "registration" \
          '{
            status: "error",
            stage: $stage,
            error: $error
          }'
        exit 1
    fi
}

# Main execution
debug_log "Starting DID Generation..."
debug_log "Using admin DID: $ADMIN_DID"
debug_log "API URL: $API_URL"

if ! generate_keypair; then
    jq -n '{
        status: "error",
        stage: "keypair",
        error: "Failed to generate keypair"
    }'
    exit 1
fi

if ! create_jwt; then
    jq -n '{
        status: "error",
        stage: "jwt",
        error: "Failed to generate valid signature"
    }'
    exit 1
fi

register_did