#!/usr/bin/env bash
# bd-grep — grep across beads issue content with snippet output
#
# Uses `bd list` for issue filtering (all bd list flags pass through),
# then greps field content for pattern matches with context.
#
# Usage:
#   bd-grep <pattern> [bd-list-flags] [--field fields] [-A N] [-B N] [-C N] [-i] [-l]
#
# Examples:
#   bd-grep "timeline"                              # open issues, description
#   bd-grep "timeline" --all                        # include closed
#   bd-grep "route" -t epic                         # epics only
#   bd-grep "fallback" --field description,design   # multiple fields
#   bd-grep "composite" -C2                         # 2 lines context
#   bd-grep "trip.id" -l                            # list matching IDs only
#   bd-grep "#1" --id yuklar-32p,yuklar-985         # specific issues
#
# Output:
#   yuklar-32p:description:23: matched line text

set -euo pipefail

FIELDS="description"
AFTER=0
BEFORE=0
CASE_FLAG=""
LIST_ONLY=false
PATTERN=""
BD_ARGS=()

args=("$@")
i=0
while [ $i -lt ${#args[@]} ]; do
  arg="${args[$i]}"
  case "$arg" in
    --field)      FIELDS="${args[$((i+1))]}"; i=$((i+2)) ;;
    --field=*)    FIELDS="${arg#--field=}"; i=$((i+1)) ;;
    -A)           AFTER="${args[$((i+1))]}"; i=$((i+2)) ;;
    -A[0-9]*)     AFTER="${arg#-A}"; i=$((i+1)) ;;
    -B)           BEFORE="${args[$((i+1))]}"; i=$((i+2)) ;;
    -B[0-9]*)     BEFORE="${arg#-B}"; i=$((i+1)) ;;
    -C)           AFTER="${args[$((i+1))]}"; BEFORE="${args[$((i+1))]}"; i=$((i+2)) ;;
    -C[0-9]*)     AFTER="${arg#-C}"; BEFORE="${arg#-C}"; i=$((i+1)) ;;
    -i)           CASE_FLAG="-i"; i=$((i+1)) ;;
    -l)           LIST_ONLY=true; i=$((i+1)) ;;
    -*)           BD_ARGS+=("$arg"); i=$((i+1))
                  if [ $i -lt ${#args[@]} ] && [[ ! "${args[$i]}" == -* ]]; then
                    BD_ARGS+=("${args[$i]}"); i=$((i+1))
                  fi ;;
    *)            if [ -z "$PATTERN" ]; then
                    PATTERN="$arg"
                  else
                    BD_ARGS+=("$arg")
                  fi
                  i=$((i+1)) ;;
  esac
done

if [ -z "$PATTERN" ]; then
  echo "Usage: bd-grep <pattern> [bd-list-flags] [--field fields] [-A N] [-B N] [-C N] [-i] [-l]" >&2
  exit 1
fi

IFS=',' read -ra FIELD_ARRAY <<< "$FIELDS"

grep_flags="-n"
[ -n "$CASE_FLAG" ] && grep_flags="$grep_flags -i"
[ "$AFTER" -gt 0 ] && grep_flags="$grep_flags -A $AFTER"
[ "$BEFORE" -gt 0 ] && grep_flags="$grep_flags -B $BEFORE"

# Extract a literal prefix from the pattern for server-side pre-filtering.
# Strip leading ^ then take chars before the first regex metachar.
literal=$(printf '%s' "$PATTERN" | sed 's/^\^//' | sed 's/[[\\.+*?{}()|^$].*//')

# Step 1: Fast issue discovery with server-side pre-filter when possible
list_args=(${BD_ARGS[@]+"${BD_ARGS[@]}"} --flat --json)

# Use --desc-contains for server-side pre-filter when searching description
# and we have a usable literal substring (at least 2 chars)
if [[ ${#FIELD_ARRAY[@]} -eq 1 && "${FIELD_ARRAY[0]}" == "description" && ${#literal} -ge 2 ]]; then
  list_args+=(--desc-contains "$literal")
fi

# Step 2: Single bd list call with pre-filter → jq extracts fields → grep matches
jq_fields_expr=""
for field in "${FIELD_ARRAY[@]}"; do
  jq_fields_expr="$jq_fields_expr \"$field\","
done
jq_fields_expr="[${jq_fields_expr%,}]"

found=false

while IFS= read -r -d '' record; do
  issue_id=$(printf '%s' "$record" | head -1)
  field=$(printf '%s' "$record" | sed -n '2p')
  content=$(printf '%s' "$record" | tail -n +3)

  matches=$(printf '%s' "$content" | grep $grep_flags -- "$PATTERN" 2>/dev/null) || continue
  found=true

  if [ "$LIST_ONLY" = true ]; then
    echo "$issue_id"
    continue
  fi

  while IFS= read -r line; do
    echo "$issue_id:$field:$line"
  done <<< "$matches"
done < <(bd list "${list_args[@]}" 2>/dev/null \
  | jq -j --argjson fields "$jq_fields_expr" '
    .[] | . as $issue |
    $fields[] | . as $field |
    ($issue[$field] // "") | select(length > 0) |
    ($issue.id + "\n" + $field + "\n" + . + "\u0000")
  ')

if [ "$found" = false ]; then
  echo "No matches found" >&2
  exit 1
fi
