#!/bin/zsh
# gt-send <id> [--key SPEC]... [--text STR]... [--enter]
# Sends input to a terminal, in argument order.
#   --key SPEC   one keystroke: "enter", "arrowDown", "c,control", "tab,control,shift"
#                Key names are camelCase Ghostty Input.Key values: a-z, digit0-9,
#                enter, escape, backspace, delete, tab, space, home, end, insert,
#                pageUp, pageDown, arrowUp/Down/Left/Right, f1-f24, comma, period,
#                slash, backslash, semicolon, quote, backquote, minus, equal,
#                bracketLeft, bracketRight, numpad0-9, numpadEnter...
#                No uppercase letters -- shifted printable chars go via --text "G".
#   --text STR   string input (paste-style / bracketed). Interior newlines do NOT
#                execute -- they stack lines in the buffer. A TRAILING newline is
#                sent as a real Enter key, so --text $'cmd\n' runs cmd, and
#                --text $'l1\nl2\n' pastes both lines then runs the block.
#                (Bracketed paste swallows a pasted trailing newline -- only a real
#                key event submits -- so this is what makes "type and run" work.)
#   --stdin      like --text but reads the block from stdin (for files/big blocks).
#                A file's trailing newline submits, so no --enter needed:
#                cat snippet.py | gt-send <id> --stdin
#   --raw STR    raw input via `perform action "text:..."` (Zig string syntax:
#                \r enter, \x1b escape). NO paste wrapping, NO key synthesis.
#                USE THIS for kitty-keyboard-protocol apps (neovim!) where
#                --key silently never arrives: gt-send <id> --raw ':wq\r'
#   --enter      shorthand for --key enter
set -euo pipefail

TID="${1:-}"; shift || true
[[ -n "$TID" ]] || { echo "usage: gt-send <id> [--key SPEC|--text STR|--enter]..." >&2; exit 2; }

# A surface can accept screen capture before it accepts input; right after
# gt-open Ghostty may transiently raise "Terminal surface model is not
# available" (-10000). Nothing is delivered when that fires, so retrying is
# safe. Any other error fails immediately.
osa_retry() {
  local out rc attempt
  for attempt in 1 2 3; do
    out=$("$@" 2>&1) && return 0
    rc=$?
    if [[ "$out" != *"not available"* && "$out" != *"-10000"* ]]; then
      print -r -- "$out" >&2; return $rc
    fi
    sleep 0.4
  done
  print -r -- "$out" >&2; return $rc
}

send_key() { osa_retry send_key_once "$@"; }
send_key_once() {  # $1 = spec like "c,control,shift"
  local key="${1%%,*}" mods=""
  [[ "$1" == *,* ]] && mods="${1#*,}"
  osascript - "$TID" "$key" "$mods" <<'EOF' >/dev/null
on run argv
    tell application "Ghostty"
        set t to terminal id (item 1 of argv)
        if item 3 of argv is "" then
            send key (item 2 of argv) to t
        else
            send key (item 2 of argv) modifiers (item 3 of argv) to t
        end if
    end tell
end run
EOF
}

send_text() { osa_retry send_text_once "$@"; }
send_text_once() {  # $1 = text (raw bracketed paste, no key synthesis)
  osascript - "$TID" "$1" <<'EOF' >/dev/null
on run argv
    tell application "Ghostty"
        input text (item 2 of argv) to (terminal id (item 1 of argv))
    end tell
end run
EOF
}

send_text_submit() {  # $1 = text; paste it, but a trailing newline becomes a real Enter
  local t="$1"
  if [[ "$t" == *$'\n' ]]; then
    t="${t%$'\n'}"               # drop exactly one trailing newline
    [[ -n "$t" ]] && send_text "$t"
    send_key "enter"             # real key event -- bracketed paste would not submit
  else
    send_text "$t"
  fi
}

send_raw() { osa_retry send_raw_once "$@"; }
send_raw_once() {  # $1 = Zig-syntax string for the text: action
  osascript - "$TID" "text:$1" <<'EOF' >/dev/null
on run argv
    tell application "Ghostty"
        perform action (item 2 of argv) on (terminal id (item 1 of argv))
    end tell
end run
EOF
}

[[ $# -gt 0 ]] || { echo "gt-send: nothing to send" >&2; exit 2; }
while [[ $# -gt 0 ]]; do
  case "$1" in
    --key)   send_key "$2"; shift 2 ;;
    --text)  send_text_submit "$2"; shift 2 ;;
    --stdin) IFS= read -rd '' _stdin || true; send_text_submit "$_stdin"; shift ;;
    --raw)   send_raw "$2"; shift 2 ;;
    --enter) send_key "enter"; shift ;;
    -h|--help) sed -n '2,7p' "$0"; exit 0 ;;
    *) echo "gt-send: unknown arg '$1'" >&2; exit 2 ;;
  esac
done
