#!/usr/bin/env bash
# Claude Code PreToolUse hook: шлёт короткую строку в Telegram перед
# каждым tool-call'ом для ощущения «активности». Дебаунс 1.5с — пока
# tool-вызовы летят пачкой, копим в /tmp буфер и шлём одним сообщением
# через 1.5 секунды тишины.
#
# Конфиг — /etc/food-market/telegram.env. Логи — /var/log/cc-tg-notify.log.
# Off-switch: создать /tmp/cc-tg-quiet — все pretool-уведомления
# скипаются (Stop hook продолжает работать).

set -u
ENV_FILE="/etc/food-market/telegram.env"
LOG_FILE="${CC_TG_LOG:-/var/log/cc-tg-notify.log}"
BUF="/tmp/cc-tg-pretool-buffer.txt"
LAST="/tmp/cc-tg-pretool-last"
LOCK="/tmp/cc-tg-pretool.lock"
QUIET_FLAG="/tmp/cc-tg-quiet"
DEBOUNCE_SEC="1.5"
MAX_LINES=20

log() { printf '%s [pretool] %s\n' "$(date -Is)" "$*" >>"$LOG_FILE" 2>/dev/null || true; }

[[ -f "$QUIET_FLAG" ]] && exit 0

if [[ -r "$ENV_FILE" ]]; then
  # shellcheck disable=SC1090
  set -a; source "$ENV_FILE"; set +a
fi
TOKEN="${TELEGRAM_BOT_TOKEN:-}"
CHAT_ID="${TELEGRAM_CHAT_ID:-}"
[[ -z "$TOKEN" || -z "$CHAT_ID" ]] && exit 0

INPUT_JSON=""
if [[ ! -t 0 ]]; then INPUT_JSON="$(cat)"; fi
[[ -z "$INPUT_JSON" ]] && exit 0

TOOL="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_name // empty' 2>/dev/null)"
[[ -z "$TOOL" || "$TOOL" == "TodoWrite" ]] && exit 0

# Извлекаем поле tool_input под нужный тип. cut -c обрезает многобайтные
# UTF-8 неаккуратно, но для urlencode результат остаётся валидным.
LINE=""
case "$TOOL" in
  Bash)
    DESC="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.description // empty' 2>/dev/null | tr '\n' ' ' | head -c 100)"
    CMD="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.command // empty' 2>/dev/null | tr '\n' ' ' | head -c 80)"
    if [[ -n "$DESC" ]]; then LINE="🔨 $DESC"; else LINE="🔨 Bash: $CMD"; fi
    ;;
  Edit)
    FP="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.file_path // empty' 2>/dev/null)"
    LINE="✏️ Edit: $(basename "${FP:-?}")"
    ;;
  Write)
    FP="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.file_path // empty' 2>/dev/null)"
    LINE="📝 Write: $(basename "${FP:-?}")"
    ;;
  Read)
    FP="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.file_path // empty' 2>/dev/null)"
    LINE="📖 Read: $(basename "${FP:-?}")"
    ;;
  Grep)
    P="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.pattern // empty' 2>/dev/null | head -c 30)"
    LINE="🔍 Grep: \"$P\""
    ;;
  Glob)
    P="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.pattern // empty' 2>/dev/null | head -c 50)"
    LINE="🌐 Glob: $P"
    ;;
  WebFetch)
    U="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.url // empty' 2>/dev/null | head -c 60)"
    LINE="🌍 Fetch: $U"
    ;;
  WebSearch)
    Q="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.query // empty' 2>/dev/null | head -c 60)"
    LINE="🔎 Search: $Q"
    ;;
  Task)
    D="$(printf '%s' "$INPUT_JSON" | jq -r '.tool_input.description // empty' 2>/dev/null | head -c 60)"
    LINE="🎯 Task: $D"
    ;;
  *)
    LINE="🔧 $TOOL"
    ;;
esac

[[ -z "$LINE" ]] && exit 0

NOW="$(date +%s%N | cut -c1-13)"

# Append + bump LAST под flock'ом — конкурентные hook'и не теряют строки.
(
  flock 9
  echo "$LINE" >> "$BUF"
  echo "$NOW" > "$LAST"
) 9>"$LOCK"

# Дебаунс-flusher в фоне. Каждый hook спавнит свой sleep, но только
# тот, чей NOW совпал с финальным LAST после задержки, реально шлёт —
# остальные тихо выходят.
(
  sleep "$DEBOUNCE_SEC"
  (
    flock 9
    LAST_TS="$(cat "$LAST" 2>/dev/null || echo 0)"
    if [[ "$LAST_TS" != "$NOW" ]]; then
      # Пришёл более свежий tool — он заfflushит сам.
      exit 0
    fi
    [[ -s "$BUF" ]] || exit 0
    # Если буфер длиннее MAX_LINES — режем хвост (свежие строки важнее).
    BODY="$(tail -n "$MAX_LINES" "$BUF")"
    : > "$BUF"
    curl -fsS -m 10 -X POST "https://api.telegram.org/bot${TOKEN}/sendMessage" \
      --data-urlencode "chat_id=${CHAT_ID}" \
      --data-urlencode "text=${BODY}" \
      --data-urlencode "disable_notification=true" \
      --data-urlencode "disable_web_page_preview=true" \
      >/dev/null 2>&1 || log "send failed"
  ) 9>"$LOCK"
) &

# Не ждём фоновую задачу — Claude Code продолжает выполнение tool'а.
disown 2>/dev/null || true
exit 0
