#!/usr/bin/env bash
# Generate an RSA key pair for a Snowflake user and store it directly in 1Password.
# Keys are generated in memory — the private key and passphrase never touch disk.
#
# Usage:
#   ./generate_keypair.sh --client acme --user permifrost --op-vault "Acme Corp" --op-title "Acme - Snowflake: permifrost"
#   ./generate_keypair.sh --client acme --user permifrost --op-vault "Acme Corp" --op-title "Acme - Snowflake: permifrost" --no-encrypt
#   ./generate_keypair.sh --client acme --user permifrost --op-vault "Acme Corp" --op-title "Acme - Snowflake: permifrost" --algorithm aes-256-cbc
#
# Prerequisites: openssl, op (1Password CLI, signed in), python3

set -euo pipefail

# --- Parse arguments ---
CLIENT=""
USER=""
OP_VAULT=""
OP_TITLE=""
ALGORITHM="des3"
NO_ENCRYPT=false

while [[ $# -gt 0 ]]; do
    case $1 in
        --client) CLIENT="$2"; shift 2 ;;
        --user) USER="$2"; shift 2 ;;
        --op-vault) OP_VAULT="$2"; shift 2 ;;
        --op-title) OP_TITLE="$2"; shift 2 ;;
        --algorithm) ALGORITHM="$2"; shift 2 ;;
        --no-encrypt) NO_ENCRYPT=true; shift ;;
        *) echo "Unknown option: $1" >&2; exit 1 ;;
    esac
done

if [[ -z "$CLIENT" || -z "$USER" || -z "$OP_VAULT" || -z "$OP_TITLE" ]]; then
    echo "Usage: $0 --client <name> --user <name> --op-vault <vault> --op-title <title> [--algorithm des3|aes-256-cbc] [--no-encrypt]" >&2
    exit 1
fi

CLIENT_LOWER=$(echo "$CLIENT" | tr '[:upper:]' '[:lower:]')
USER_LOWER=$(echo "$USER" | tr '[:upper:]' '[:lower:]')
USER_UPPER=$(echo "$USER" | tr '[:lower:]' '[:upper:]')
PRIVATE_KEY_NAME="${CLIENT_LOWER}_${USER_LOWER}_snowflake"
PUBLIC_KEY_NAME="${CLIENT_LOWER}_${USER_LOWER}_snowflake"

echo "Snowflake Key Pair Generator (1Password mode)"
echo "==============================================="
echo "  Client:     $CLIENT"
echo "  User:       $USER"
echo "  Encrypted:  $(if $NO_ENCRYPT; then echo 'no'; else echo "yes ($ALGORITHM)"; fi)"
echo "  1Password:  $OP_TITLE (vault: $OP_VAULT)"
echo "  Mode:       in-memory (no files written to disk)"
echo ""

# --- Generate passphrase (in memory, never printed) ---
PASSPHRASE=""
if ! $NO_ENCRYPT; then
    PASSPHRASE=$(python3 -c "import secrets,string; print(''.join(secrets.choice(string.ascii_letters+string.digits) for _ in range(40)))")
fi

# --- Generate keys in memory ---
echo "Generating keys (in memory)..."
if $NO_ENCRYPT; then
    PRIVATE_KEY=$(openssl genrsa 2048 2>/dev/null | openssl pkcs8 -topk8 -nocrypt -inform PEM -outform PEM 2>/dev/null)
else
    PRIVATE_KEY=$(openssl genrsa 2048 2>/dev/null | openssl pkcs8 -topk8 -v2 "$ALGORITHM" -inform PEM -outform PEM -passout "pass:$PASSPHRASE" 2>/dev/null)
fi
echo "  Private key generated (in memory)"

if $NO_ENCRYPT; then
    PUBLIC_KEY=$(echo "$PRIVATE_KEY" | openssl rsa -pubout 2>/dev/null)
else
    PUBLIC_KEY=$(echo "$PRIVATE_KEY" | openssl rsa -pubout -passin "pass:$PASSPHRASE" 2>/dev/null)
fi
echo "  Public key generated (in memory)"

# --- Print public key ---
echo ""
echo "============================================================"
echo "PUBLIC KEY (copy this into permifrost.yml rsa_public_key):"
echo "============================================================"
echo "$PUBLIC_KEY"

# --- Store to 1Password ---
echo ""
echo "Storing to 1Password..."

# Pass key data via env vars to avoid shell quoting issues with PEM content.
# Use `op item template get | python3 | op item create` pipeline — op item create
# requires a pipeline originating from an op command to read stdin correctly in
# non-tty environments (e.g. Claude Code, CI).
trap 'unset _OP_PRIVATE_KEY _OP_PUBLIC_KEY _OP_PASSPHRASE _OP_TITLE _OP_USER_UPPER _OP_PRIVATE_KEY_NAME _OP_PUBLIC_KEY_NAME' EXIT
export _OP_PRIVATE_KEY="$PRIVATE_KEY"
export _OP_PUBLIC_KEY="$PUBLIC_KEY"
export _OP_PASSPHRASE="$PASSPHRASE"
export _OP_TITLE="$OP_TITLE"
export _OP_USER_UPPER="$USER_UPPER"
export _OP_PRIVATE_KEY_NAME="${PRIVATE_KEY_NAME}_private_key"
export _OP_PUBLIC_KEY_NAME="${PUBLIC_KEY_NAME}_public_key"

op item template get Login --format json | python3 -c "
import json, sys, os
template = json.load(sys.stdin)
template['title'] = os.environ['_OP_TITLE']
template['sections'] = [{'id': 'keys', 'label': 'Key Files'}]
passphrase = os.environ['_OP_PASSPHRASE']
fields = [
    {'id': 'username', 'type': 'STRING', 'purpose': 'USERNAME', 'label': 'username', 'value': os.environ['_OP_USER_UPPER']},
]
if passphrase:
    fields.append({'id': 'passphrase', 'type': 'CONCEALED', 'label': 'passphrase', 'value': passphrase})
fields.append({'id': os.environ['_OP_PRIVATE_KEY_NAME'], 'section': {'id': 'keys'}, 'type': 'CONCEALED', 'label': os.environ['_OP_PRIVATE_KEY_NAME'], 'value': os.environ['_OP_PRIVATE_KEY']})
fields.append({'id': os.environ['_OP_PUBLIC_KEY_NAME'], 'section': {'id': 'keys'}, 'type': 'STRING', 'label': os.environ['_OP_PUBLIC_KEY_NAME'], 'value': os.environ['_OP_PUBLIC_KEY']})
template['fields'] = fields
print(json.dumps(template))
" | op item create --vault "$OP_VAULT" -

unset _OP_PRIVATE_KEY _OP_PUBLIC_KEY _OP_PASSPHRASE _OP_TITLE _OP_USER_UPPER _OP_PRIVATE_KEY_NAME _OP_PUBLIC_KEY_NAME

echo ""
echo "  Stored in 1Password: $OP_TITLE (vault: $OP_VAULT)"
echo ""
echo "NEXT STEPS:"
echo "  1. Copy the public key above into the rsa_public_key field"
echo "     of the '$USER' user in permifrost.yml"
