#!/bin/bash
# forms/spawn-intake.sh — Spawn a multi-field intake form using grabchars.
#
# Usage:
#   spawn-intake.sh FIELDS_JSON [title]
#
# FIELDS_JSON is a path to a JSON file containing an array of field specs:
#
#   [
#     { "name": "app_name",  "type": "text",      "prompt": "App name: ",   "maxlen": 30 },
#     { "name": "port",      "type": "masked",     "prompt": "Port: ",       "mask": "nnnn", "default": "8080" },
#     { "name": "env",       "type": "select",     "prompt": "Environment: ","choices": "dev,staging,prod" },
#     { "name": "region",    "type": "select-lr",  "prompt": "Region: ",     "choices": "us-east,us-west,eu" },
#     { "name": "token",     "type": "secret",     "prompt": "API token: ",  "maxlen": 64 },
#     { "name": "confirm",   "type": "yn",         "prompt": "Confirm? ",    "default": "y", "timeout": 15 }
#   ]
#
# Field types:
#   text      Free text, up to maxlen chars, Enter to submit
#   masked    Format-validated input via grabchars -m (mask required)
#   select    Vertical filter-as-you-type menu (choices required)
#   select-lr Horizontal left/right menu (choices required)
#   secret    Silent input (no echo), up to maxlen chars
#   yn        Single y/n keypress (default and timeout optional)
#
# Returns JSON on stdout:
#   {"status":"submitted","data":{"app_name":"myapp","port":"8080",...}}
#   {"status":"cancelled"}    # user pressed Escape on any field
#   {"status":"error","message":"..."}
#
# Exit code: 0 = submitted or cancelled, 1 = error

set -euo pipefail

FIELDS_JSON="${1:?Usage: spawn-intake.sh FIELDS_JSON [title]}"
TITLE="${2:-Form}"

[[ -f "$FIELDS_JSON" ]] || {
    printf '{"status":"error","message":"fields file not found: %s"}\n' "$FIELDS_JSON"
    exit 1
}

GRABCHARS=$(command -v grabchars 2>/dev/null) || {
    printf '{"status":"error","message":"grabchars not found in PATH"}\n'
    exit 1
}

JQ=$(command -v jq 2>/dev/null) || {
    printf '{"status":"error","message":"jq not found in PATH (required for spawn-intake)"}\n'
    exit 1
}

# Validate that FIELDS_JSON is valid JSON array
"$JQ" -e 'if type == "array" then true else error("expected array") end' \
    "$FIELDS_JSON" > /dev/null 2>&1 || {
    printf '{"status":"error","message":"fields file is not a valid JSON array"}\n'
    exit 1
}

OUTPUT_FILE=$(mktemp /tmp/gc-intake-XXXXXX.json)
DONE_FILE="${OUTPUT_FILE}.done"
RUNNER=$(mktemp /tmp/gc-intake-runner-XXXXXX.sh)

cleanup() {
    rm -f "$OUTPUT_FILE" "$DONE_FILE" "$RUNNER"
    if [[ -n "${WIN_ID:-}" ]]; then
        osascript << APPLEEOF 2>/dev/null || true
tell application "iTerm2"
    try
        close (windows whose id is $WIN_ID) saving no
    end try
end tell
APPLEEOF
    fi
}
trap cleanup EXIT

rm -f "$OUTPUT_FILE" "$DONE_FILE"

# ── Generate the runner script ────────────────────────────────────────────────
#
# We parse the fields JSON here (in the outer script, which has jq and PATH)
# and generate a flat zsh runner with one grabchars call per field.
# The runner uses absolute paths for grabchars and jq — no PATH needed.

{
    printf '#!/bin/zsh -f\n'
    printf '# Auto-generated by spawn-intake.sh — do not edit\n\n'
    printf 'GRABCHARS=%s\n' "$(printf '%q' "$GRABCHARS")"
    printf 'JQ=%s\n'        "$(printf '%q' "$JQ")"
    printf 'OUTPUT=%s\n'    "$(printf '%q' "$OUTPUT_FILE")"
    printf 'DONE=%s\n'      "$(printf '%q' "$DONE_FILE")"
    printf '\n'

    printf 'clear\n'
    printf 'printf "\\n  %s\\n\\n" %s\n' "$TITLE" "$(printf '%q' "$(printf '%.0s─' {1..40})")"

    # Read each field from the JSON array and emit the corresponding grabchars call
    FIELD_COUNT=$("$JQ" 'length' "$FIELDS_JSON")
    FIELD_NAMES=()

    for (( i=0; i<FIELD_COUNT; i++ )); do
        field=$("$JQ" -c ".[$i]" "$FIELDS_JSON")

        name=$(    echo "$field" | "$JQ" -r '.name')
        type=$(    echo "$field" | "$JQ" -r '.type // "text"')
        prompt=$(  echo "$field" | "$JQ" -r '.prompt')
        maxlen=$(  echo "$field" | "$JQ" -r '.maxlen // 80')
        mask=$(    echo "$field" | "$JQ" -r '.mask // ""')
        choices=$( echo "$field" | "$JQ" -r '.choices // ""')
        default=$( echo "$field" | "$JQ" -r '.default // ""')
        timeout=$( echo "$field" | "$JQ" -r '.timeout // 0')

        FIELD_NAMES+=("$name")

        # Field counter prefix: "(1/4) "
        COUNTER="($((i+1))/$FIELD_COUNT) "
        FULL_PROMPT="  ${COUNTER}${prompt}"

        # Emit the grabchars call, capturing stdout to V_<name>
        case "$type" in
            text)
                printf 'V_%s=$("$GRABCHARS" -n %s -r -q %s 2>/dev/tty)\n' \
                    "$name" "$maxlen" "$(printf '%q' "$FULL_PROMPT")"
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            masked)
                [[ -z "$mask" ]] && {
                    printf '# ERROR: field %s has type "masked" but no mask defined\n' "$name"
                    printf 'V_%s=""\n' "$name"
                    continue
                }
                # Auto-append mask pattern to prompt so user knows the expected format
                MASKED_PROMPT="${FULL_PROMPT% }  [${mask}] "
                printf 'V_%s=$("$GRABCHARS" -m %s -q %s 2>/dev/tty)\n' \
                    "$name" "$(printf '%q' "$mask")" "$(printf '%q' "$MASKED_PROMPT")"
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            select)
                [[ -z "$choices" ]] && {
                    printf '# ERROR: field %s has type "select" but no choices defined\n' "$name"
                    printf 'V_%s=""\n' "$name"
                    continue
                }
                DEFAULT_FLAG=""
                [[ -n "$default" ]] && DEFAULT_FLAG="-d $(printf '%q' "$default")"
                printf 'V_%s=$("$GRABCHARS" select %s %s -q %s 2>/dev/tty)\n' \
                    "$name" "$(printf '%q' "$choices")" "$DEFAULT_FLAG" "$(printf '%q' "$FULL_PROMPT")"
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            select-lr)
                [[ -z "$choices" ]] && {
                    printf '# ERROR: field %s has type "select-lr" but no choices defined\n' "$name"
                    printf 'V_%s=""\n' "$name"
                    continue
                }
                DEFAULT_FLAG=""
                [[ -n "$default" ]] && DEFAULT_FLAG="-d $(printf '%q' "$default")"
                printf 'V_%s=$("$GRABCHARS" select-lr %s %s -q %s 2>/dev/tty)\n' \
                    "$name" "$(printf '%q' "$choices")" "$DEFAULT_FLAG" "$(printf '%q' "$FULL_PROMPT")"
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            secret)
                printf 'V_%s=$("$GRABCHARS" -n %s -r -s -e -q %s 2>/dev/tty)\n' \
                    "$name" "$maxlen" "$(printf '%q' "$FULL_PROMPT")"
                printf 'printf "\\n" >&2\n'
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            yn)
                YNFLAGS="-c yn"
                [[ -n "$default" ]] && YNFLAGS="$YNFLAGS -d $(printf '%q' "$default")"
                [[ "$timeout" -gt 0 ]] && YNFLAGS="$YNFLAGS -t $timeout"
                printf 'V_%s=$("$GRABCHARS" %s -b -q %s 2>/dev/tty)\n' \
                    "$name" "$YNFLAGS" "$(printf '%q' "$FULL_PROMPT")"
                printf 'printf "\\n" >&2\n'
                printf '[[ $? -eq 255 ]] && { printf '"'"'{"status":"cancelled"}'"'"' > "$OUTPUT"; printf done > "$DONE"; exit 0; }\n'
                ;;
            *)
                printf '# Unknown field type "%s" for field %s — skipping\n' "$type" "$name"
                printf 'V_%s=""\n' "$name"
                ;;
        esac

        printf 'printf "\\n"\n'
    done

    # Emit JSON assembly using jq
    printf '\n# Assemble result JSON\n'
    printf '"$JQ" -n'

    for name in "${FIELD_NAMES[@]}"; do
        printf ' --arg %s "$V_%s"' "$name" "$name"
    done

    printf ' '"'"'{"status":"submitted","data":{'
    FIRST=1
    for name in "${FIELD_NAMES[@]}"; do
        [[ $FIRST -eq 0 ]] && printf ','
        printf '"%s":$%s' "$name" "$name"
        FIRST=0
    done
    printf '}}'"'"' > "$OUTPUT"\n'

    printf 'printf done > "$DONE"\n'

} > "$RUNNER"
chmod +x "$RUNNER"

# ── Spawn the window ──────────────────────────────────────────────────────────

ESC_RUNNER=$(printf '%s' "$RUNNER" | sed "s/'/'\\\\''/g")
ESC_TITLE=$(printf '%s' "$TITLE" | sed "s/'/'\\\\''/g")

WIN_ID=$(osascript << APPLEEOF
tell application "iTerm2"
    set newWin to (create window with default profile command "/bin/zsh -f '${ESC_RUNNER}'")
    tell current session of newWin
        set name to "📝 ${ESC_TITLE}"
    end tell
    return id of newWin
end tell
APPLEEOF
) || {
    printf '{"status":"error","message":"failed to spawn iTerm2 window"}\n'
    exit 1
}

# Poll — no hard timeout since form length is variable; cap at 10 minutes
MAX_POLLS=$(( 10 * 60 * 5 ))
elapsed=0
while [[ ! -f "$DONE_FILE" ]]; do
    sleep 0.2
    elapsed=$(( elapsed + 1 ))
    if [[ $elapsed -gt $MAX_POLLS ]]; then
        printf '{"status":"error","message":"timed out waiting for form completion"}\n'
        exit 1
    fi
done

cat "$OUTPUT_FILE"
