#!/usr/bin/env bash
# lmux — Linux tmux wrapper with a CLI surface compatible with cmux/wmux.
# Surface refs are formatted "surface:N" or "session:N" where N is a tmux
# pane index. The wrapper extracts N and calls tmux with `-t N`.

set -euo pipefail

_die() { echo "lmux: $*" >&2; exit 2; }

# Extract the trailing pane index from a surface ref like "surface:3" or "main:3".
_surface_index() {
  local ref="$1"
  case "$ref" in
    *:*) echo "${ref##*:}" ;;
    *)   _die "invalid surface ref: $ref (expected surface:N)" ;;
  esac
}

# Parse a flag pair "--name value" out of an arg list, echoing the value.
# Usage: val=$(_get_flag --surface "$@")
_get_flag() {
  local name="$1"; shift
  while [ $# -gt 0 ]; do
    if [ "$1" = "$name" ]; then
      echo "$2"
      return 0
    fi
    shift
  done
  return 1
}

cmd_send() {
  local surface text
  surface=$(_get_flag --surface "$@") || _die "send: missing --surface"
  # Last positional arg is the text payload.
  text=""
  while [ $# -gt 0 ]; do
    case "$1" in
      --surface) shift 2 ;;
      *) text="$1"; shift ;;
    esac
  done
  [ -z "$text" ] && _die "send: missing text"
  local idx
  idx=$(_surface_index "$surface")

  if [[ "$text" == *'\n' ]]; then
    local body="${text%\\n}"
    tmux send-keys -t "$idx" "$body" Enter
  else
    tmux send-keys -t "$idx" "$text"
  fi
}

cmd_send_key() {
  local surface key
  surface=$(_get_flag --surface "$@") || _die "send-key: missing --surface"
  key=""
  while [ $# -gt 0 ]; do
    case "$1" in
      --surface) shift 2 ;;
      *) key="$1"; shift ;;
    esac
  done
  [ -z "$key" ] && _die "send-key: missing key name"
  local idx
  idx=$(_surface_index "$surface")

  local mapped
  case "$key" in
    return|enter)  mapped="Enter" ;;
    escape|esc)    mapped="Escape" ;;
    tab)           mapped="Tab" ;;
    space)         mapped="Space" ;;
    backspace|bs)  mapped="BSpace" ;;
    up)            mapped="Up" ;;
    down)          mapped="Down" ;;
    left)          mapped="Left" ;;
    right)         mapped="Right" ;;
    ctrl+*)        mapped="C-${key#ctrl+}" ;;
    alt+*)         mapped="M-${key#alt+}" ;;
    *)             _die "send-key: unknown key '$key'" ;;
  esac

  tmux send-keys -t "$idx" "$mapped"
}

cmd_read_screen() {
  local surface scrollback=0 lines=""
  surface=$(_get_flag --surface "$@") || _die "read-screen: missing --surface"
  while [ $# -gt 0 ]; do
    case "$1" in
      --surface)     shift 2 ;;
      --scrollback)  scrollback=1; shift ;;
      --lines)       lines="$2"; shift 2 ;;
      *)             shift ;;
    esac
  done
  local idx
  idx=$(_surface_index "$surface")

  if [ -n "$lines" ]; then
    tmux capture-pane -t "$idx" -p -S "-${lines}"
  elif [ "$scrollback" = "1" ]; then
    tmux capture-pane -t "$idx" -p -S -
  else
    tmux capture-pane -t "$idx" -p
  fi
}

cmd_new_split() {
  local dir="${1:-}"
  local flag
  case "$dir" in
    right) flag="-h" ;;
    down)  flag="-v" ;;
    *)     _die "new-split: direction must be 'right' or 'down'" ;;
  esac
  tmux split-window "$flag" -P -F "surface:#{pane_index}"
}

cmd_close_surface() {
  local surface
  surface=$(_get_flag --surface "$@") || _die "close-surface: missing --surface"
  local idx
  idx=$(_surface_index "$surface")
  tmux kill-pane -t "$idx"
}

cmd_tree() {
  # Flags ignored beyond --all --json; the only supported mode.
  local fmt="#{session_name}:#{window_index}:#{pane_index}:#{pane_title}"
  if command -v jq >/dev/null 2>&1; then
    tmux list-panes -a -F "$fmt" \
      | jq -R 'select(length > 0) | split(":") | {session: .[0], window: (.[1]|tonumber), pane: (.[2]|tonumber), surface: ("surface:" + .[2]), title: (.[3] // "")}' \
      | jq -s .
  elif command -v python3 >/dev/null 2>&1; then
    tmux list-panes -a -F "$fmt" \
      | python3 -c "import sys,json; rows=[l.split(':',3) for l in sys.stdin.read().splitlines() if l]; print(json.dumps([{'session':r[0],'window':int(r[1]),'pane':int(r[2]),'surface':f'surface:{r[2]}','title':r[3] if len(r)>3 else ''} for r in rows]))"
  else
    echo "lmux tree: warning — neither jq nor python3 found, falling back to naive JSON escape (titles with special chars may break)" >&2
    local raw
    raw=$(tmux list-panes -a -F "$fmt")
    printf '['
    local first=1
    while IFS= read -r line; do
      [ -z "$line" ] && continue
      IFS=':' read -r session window pane title <<<"$line"
      [ "$first" = "1" ] || printf ','
      first=0
      local esc_title
      esc_title=${title//\\/\\\\}
      esc_title=${esc_title//\"/\\\"}
      printf '{"session":"%s","window":%s,"pane":%s,"surface":"surface:%s","title":"%s"}' \
        "$session" "$window" "$pane" "$pane" "$esc_title"
    done <<<"$raw"
    printf ']\n'
  fi
}

cmd_identify() {
  if [ -n "${TMUX:-}" ]; then
    echo "tmux: ${TMUX}"
    exit 0
  else
    echo "lmux: not inside a tmux session (TMUX env var unset)" >&2
    exit 1
  fi
}

cmd_list_workspaces() {
  tmux list-windows -F "workspace:#{window_index} #{window_name}"
}

cmd_notify() {
  local title="" body=""
  while [ $# -gt 0 ]; do
    case "$1" in
      --title) title="$2"; shift 2 ;;
      --body)  body="$2";  shift 2 ;;
      *) shift ;;
    esac
  done
  if command -v notify-send >/dev/null 2>&1; then
    notify-send "$title" "$body"
  else
    echo "[lmux notify] $title: $body"
  fi
}

main() {
  local sub="${1:-}"
  shift || true
  case "$sub" in
    send)            cmd_send "$@" ;;
    send-key)        cmd_send_key "$@" ;;
    read-screen)     cmd_read_screen "$@" ;;
    new-split)       cmd_new_split "$@" ;;
    close-surface)   cmd_close_surface "$@" ;;
    tree)            cmd_tree "$@" ;;
    identify)        cmd_identify "$@" ;;
    list-workspaces) cmd_list_workspaces "$@" ;;
    notify)          cmd_notify "$@" ;;
    ""|-h|--help)
      cat <<EOF
lmux — Linux tmux wrapper (cmux/wmux-compatible CLI)

Commands:
  send --surface S:N "text\\n"
  send-key --surface S:N <return|escape|tab|ctrl+c|...>
  read-screen --surface S:N [--scrollback | --lines N]
  new-split <right|down>
  close-surface --surface S:N
  tree --all --json
  identify
  list-workspaces
  notify --title T --body B
EOF
      ;;
    *) _die "unknown subcommand: $sub" ;;
  esac
}

main "$@"
