#!/usr/bin/env bash
# PreToolUse hook: sugiere funciones del registry cuando un comando Bash
# inline probablemente reinventa una funcion existente (issue 0087).
#
# Llama a `./fn match "<cmd>"` con timeout 200ms. Si encaja con alta
# confianza, imprime un <system-reminder> a stderr para que Claude Code
# lo lea como recordatorio. NUNCA bloquea la tool — exit 0 siempre.

set -euo pipefail

# ---- Always exit 0, no matter what ----
trap 'exit 0' ERR

# ---- Resolve registry root (walks up from cwd) ----
resolve_root() {
    local d="${PWD}"
    while [ "$d" != "/" ]; do
        if [ -f "$d/registry.db" ]; then
            printf '%s' "$d"
            return 0
        fi
        d=$(dirname "$d")
    done
    return 1
}

ROOT=$(resolve_root) || exit 0
FN_BIN="$ROOT/fn"
[ -x "$FN_BIN" ] || exit 0

# ---- Read stdin JSON ----
command -v jq >/dev/null 2>&1 || exit 0
INPUT=$(cat)
[ -z "$INPUT" ] && exit 0

TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
[ "$TOOL_NAME" = "Bash" ] || exit 0

CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
[ -z "$CMD" ] && exit 0

# Single-line for matching against denylist patterns
CMD_FLAT=$(printf '%s' "$CMD" | tr '\n' ' ')

# ---- Denylist (skip antes de llamar fn match para ahorrar el invoke) ----

# Comandos demasiado cortos -> trivial
CMD_LEN=${#CMD_FLAT}
[ "$CMD_LEN" -lt 20 ] && exit 0

# Trivial single-utility commands
case "$CMD_FLAT" in
    "ls"|"ls "*|"cd"|"cd "*|"pwd"|"pwd "*|"cat"|"cat "*|"echo"|"echo "*)
        exit 0 ;;
    "grep"|"grep "*|"head"|"head "*|"tail"|"tail "*|"wc"|"wc "*)
        exit 0 ;;
    "mkdir"|"mkdir "*|"rm"|"rm "*|"mv"|"mv "*|"cp"|"cp "*)
        exit 0 ;;
    "git"|"git "*)
        exit 0 ;;
    "go"|"go "*)
        # go build / go test corrientes — el agente ya los maneja
        exit 0 ;;
esac

# Comandos que ya usan el registry: ./fn ..., fn run ..., mcp__registry__*
if printf '%s' "$CMD_FLAT" | grep -qE '(^|[[:space:]])\./fn([[:space:]]|$)'; then
    exit 0
fi
if printf '%s' "$CMD_FLAT" | grep -qE '(^|[[:space:]])fn[[:space:]]+(run|search|show|code|uses|doctor|index|match|list|add|proposal|sync|ops|check)'; then
    exit 0
fi

# Pure-cd (movement only, no logic)
if printf '%s' "$CMD_FLAT" | grep -qE '^[[:space:]]*cd[[:space:]]+[^&|;]+$'; then
    exit 0
fi

# ---- Llamar fn match con timeout 200ms ----
command -v timeout >/dev/null 2>&1 || exit 0

# Truncar el comando a algo razonable para fn match (evitar args huge)
CMD_TRUNC=$(printf '%s' "$CMD_FLAT" | head -c 500)

MATCH_JSON=$(timeout 0.2 "$FN_BIN" match "$CMD_TRUNC" --format json --top 3 2>/dev/null) || exit 0
[ -z "$MATCH_JSON" ] && exit 0

# ---- Parsear JSON ----
HIGH_CONF=$(printf '%s' "$MATCH_JSON" | jq -r '.high_confidence // false' 2>/dev/null || echo "false")
TOP_ID=$(printf '%s' "$MATCH_JSON" | jq -r '.top[0].id // ""' 2>/dev/null || echo "")
TOP_SCORE=$(printf '%s' "$MATCH_JSON" | jq -r '.top[0].score // 0' 2>/dev/null || echo "0")
TOP_SIG=$(printf '%s' "$MATCH_JSON" | jq -r '.top[0].signature // ""' 2>/dev/null || echo "")
TOP_SNIP=$(printf '%s' "$MATCH_JSON" | jq -r '.top[0].snippet // ""' 2>/dev/null || echo "")

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

# Trigger condition: (high_confidence==true OR score>=0.85) AND score>=0.6
# - high_confidence requires top1/top2 gap > 1.5 (set por fn match)
# - score>=0.85 cubre matches muy fuertes donde el gap es modesto
SCORE_HI=$(awk -v s="$TOP_SCORE" 'BEGIN{ print (s+0 >= 0.85) ? "1" : "0" }')
SCORE_MIN=$(awk -v s="$TOP_SCORE" 'BEGIN{ print (s+0 >= 0.6) ? "1" : "0" }')

[ "$SCORE_MIN" = "1" ] || exit 0
if [ "$HIGH_CONF" != "true" ] && [ "$SCORE_HI" != "1" ]; then
    exit 0
fi

# Truncar snippet a 100 chars y limpiar saltos de linea
SNIP_SHORT=$(printf '%s' "$TOP_SNIP" | tr '\n' ' ' | head -c 100)

# Formatear score con 2 decimales
SCORE_FMT=$(awk -v s="$TOP_SCORE" 'BEGIN{ printf "%.2f", s+0 }')

# ---- Emitir <system-reminder> a stderr ----
cat >&2 <<EOF
<system-reminder>FUZZY-MATCH (issue 0087): your Bash command may already be a function.
USE: ./fn run $TOP_ID  ->  $TOP_SIG
SNIPPET: $SNIP_SHORT
Confidence: $SCORE_FMT. If you proceed inline, the violation will be logged.
</system-reminder>
EOF

exit 0

# Test manual:
# echo '{"tool_name":"Bash","tool_input":{"command":"taskkill.exe /IM registry_dashboard.exe /F"},"session_id":"test"}' \
#   | bash .claude/scripts/hook_fn_match.sh
#
# Casos silenciosos:
# echo '{"tool_name":"Bash","tool_input":{"command":"ls -la"},"session_id":"test"}' \
#   | bash .claude/scripts/hook_fn_match.sh
# echo '{"tool_name":"Bash","tool_input":{"command":"./fn run filter_slice_go_core 1 2 3"},"session_id":"test"}' \
#   | bash .claude/scripts/hook_fn_match.sh
