#!/usr/bin/env bash
# aTool - hooks/doc-sync-reminder
# PostToolUse hook: reminds AI to update docs after source file modifications
# Only triggers on Write|Edit of source files (not .md/.json/.css/.lock etc.)

set -euo pipefail

# Escape string for JSON embedding
escape_for_json() {
    local s="$1"
    s="${s//\\/\\\\}"
    s="${s//\"/\\\"}"
    s="${s//$'\n'/\\n}"
    s="${s//$'\r'/\\r}"
    s="${s//$'\t'/\\t}"
    printf '%s' "$s"
}

main() {
    # Read tool input from stdin (Claude Code provides JSON on stdin for PostToolUse)
    local INPUT=""
    if [[ ! -t 0 ]]; then
        INPUT=$(cat)
    fi

    # ── Fast filter: extract file path from tool input (< 5ms) ───────────────

    local TOOL_NAME=""
    local FILE_PATH=""
    if command -v jq &>/dev/null && [[ -n "$INPUT" ]]; then
        TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
        FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
    elif [[ -n "$INPUT" ]]; then
        # Fallback: extract tool_name and file_path using grep/sed (jq not available)
        TOOL_NAME=$(printf '%s' "$INPUT" | grep -oE '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"tool_name"[[:space:]]*:[[:space:]]*"//;s/"$//' || echo "")
        FILE_PATH=$(printf '%s' "$INPUT" | grep -oE '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//;s/"$//' || echo "")
    fi

    # Only respond to Write/Edit/MultiEdit
    if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "MultiEdit" ]]; then
        exit 0
    fi

    # No file path extracted — exit silently
    if [[ -z "$FILE_PATH" ]]; then
        exit 0
    fi

    # ── Source file detection: only trigger on actual source code ─────────────

    local FILE_EXT="${FILE_PATH##*.}"
    local FILE_BASE
    FILE_BASE=$(basename "$FILE_PATH")

    # Skip non-source files
    case "$FILE_EXT" in
        md|markdown|txt|rst) exit 0 ;;
        json|yaml|yml|toml|xml) exit 0 ;;
        css|scss|less|sass|styl) exit 0 ;;
        lock|map|log) exit 0 ;;
        svg|png|jpg|jpeg|gif|ico|webp|ttf|woff|woff2|eot) exit 0 ;;
        gitignore|editorconfig|prettierrc|eslintrc) exit 0 ;;
    esac

    # Skip config files and dotfiles
    case "$FILE_BASE" in
        .*|*.config.*|tsconfig.*|jsconfig.*|vite.config.*|next.config.*|tailwind.config.*|postcss.config.*|webpack.config.*) exit 0 ;;
    esac

    # Only trigger on known source extensions
    case "$FILE_EXT" in
        ts|tsx|js|jsx|vue|svelte) ;;   # Web
        html) ;;                        # HTML templates
        rs) ;;                          # Rust
        py) ;;                          # Python
        go) ;;                          # Go
        java|kt|kts) ;;                 # Java/Kotlin
        swift) ;;                       # Swift
        dart) ;;                        # Dart
        ets) ;;                         # HarmonyOS ArkTS
        sh|bash) ;;                     # Shell
        *) exit 0 ;;                     # Unknown extension, skip
    esac

    # ── Stale doc check: remind if any docs are actually stale ────────────────

    local PROJECT_DIR="${PWD:-}"

    # Source common.sh for count_stale_docs, and registry.sh (if present
    # alongside it) for resolver-based skill emission. When registry.sh is
    # found, disabled skills are suppressed from the reminder; when it is
    # absent, the reminder falls back to its full static text. The hook's own
    # sibling lib/ (dev/worktree layout) is searched so registry.sh is found
    # without ATOOL_ROOT being exported.
    local _LIB_FOUND=false
    local _REGISTRY_LOADED=false
    local _HOOK_DIR
    _HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    for _lib_dir in "${ATOOL_ROOT:-}/lib" "${_HOOK_DIR}/../lib" "$HOME/.claude/lib" "$HOME/.cursor/lib"; do
        if [[ -f "${_lib_dir}/common.sh" ]]; then
            # shellcheck source=/dev/null
            source "${_lib_dir}/common.sh"
            _LIB_FOUND=true
            if [[ -f "${_lib_dir}/registry.sh" ]]; then
                # shellcheck source=/dev/null
                source "${_lib_dir}/registry.sh"
                _REGISTRY_LOADED=true
            fi
            break
        fi
    done

    # _skill_ref SKILL_NAME — emit "/SKILL_NAME" if the skill should be
    # referenced, or "" to suppress it (mirrors session-start floor, spec §5.3):
    #   registry DATA available + enabled        → "/SKILL_NAME"
    #   registry DATA available + disabled/absent → ""  (suppress dangling ref)
    #   registry UNAVAILABLE (no lib / no json)  → "/SKILL_NAME"  (static floor)
    _skill_ref() {
        local skill_name="$1"
        if $_REGISTRY_LOADED && registry_load 2>/dev/null; then
            if skill_enabled "$skill_name" 2>/dev/null; then
                printf '/%s' "$skill_name"
            fi
        else
            printf '/%s' "$skill_name"
        fi
    }

    local STALE=false
    if $_LIB_FOUND && declare -f count_stale_docs &>/dev/null; then
        # Check against all common documentation files — any stale doc triggers reminder
        local DOC_FILES=()
        for doc in "README.md" "COMPONENT.md" "UI_STYLE.md" "DESIGN.md" "ARCHITECTURE.md" "CODE_REVIEW.md"; do
            [[ -f "$PROJECT_DIR/$doc" ]] && DOC_FILES+=("$doc")
        done
        # Also check docs/ directory markdown files (up to 20)
        if [[ -d "$PROJECT_DIR/docs" ]]; then
            while IFS= read -r doc; do
                [[ -n "$doc" ]] && DOC_FILES+=("$doc")
            done < <(find "$PROJECT_DIR/docs" -maxdepth 1 -name '*.md' -type f 2>/dev/null | head -20 | sed "s|^$PROJECT_DIR/||")
        fi

        if [[ ${#DOC_FILES[@]} -gt 0 ]]; then
            local STALE_COUNT
            STALE_COUNT=$(count_stale_docs "$PROJECT_DIR" "${DOC_FILES[@]}")
            if [[ "$STALE_COUNT" -gt 0 ]]; then
                STALE=true
            fi
        fi
    fi

    # If no stale doc detection available, exit silently (consistent with task-state-tracker fallback)
    if ! $_LIB_FOUND; then
        exit 0
    fi

    if ! $STALE; then
        exit 0  # Docs are up to date, no reminder needed
    fi

    # ── Output reminder ──────────────────────────────────────────────────────
    # Skill references are resolved per-skill: a DISABLED skill is omitted from
    # its sentence (no dangling slash-command), while ENABLED skills (and the
    # registry-unavailable fallback) keep their references.

    local _VBC_REF _COAUTHOR_REF _ENFORCER_REF
    _VBC_REF=$(_skill_ref "verification-before-completion")
    _COAUTHOR_REF=$(_skill_ref "doc-coauthoring")
    _ENFORCER_REF=$(_skill_ref "doc-standards-enforcer")

    # Item 2: only mention completion-verification when its skill is available.
    local _LINE2=""
    if [[ -n "$_VBC_REF" ]]; then
        _LINE2="
2. Before claiming this task done, invoke ${_VBC_REF} to verify doc freshness."
    fi

    # Item 3: compose from whichever doc skills are available.
    local _LINE3=""
    if [[ -n "$_COAUTHOR_REF" && -n "$_ENFORCER_REF" ]]; then
        _LINE3="
3. For substantial doc updates, invoke ${_COAUTHOR_REF}. For doc format validation, invoke ${_ENFORCER_REF}."
    elif [[ -n "$_COAUTHOR_REF" ]]; then
        _LINE3="
3. For substantial doc updates, invoke ${_COAUTHOR_REF}."
    elif [[ -n "$_ENFORCER_REF" ]]; then
        _LINE3="
3. For doc format validation, invoke ${_ENFORCER_REF}."
    fi

    local _REMINDER="<ATOOL-DOC-SYNC-REMINDER>
You just modified a source file. Documentation may now be stale.

IMMEDIATE actions required (do NOT defer):
1. Update affected docs: README.md (architecture/structure), COMPONENT.md (API/usage), UI_STYLE.md (styles), DESIGN.md (design system tokens), docs/ (knowledge base).${_LINE2}${_LINE3}

Gate rule: if any doc is stale, the task is NOT done.
</ATOOL-DOC-SYNC-REMINDER>"

    # Use structured JSON output for Claude Code PostToolUse hooks
    # Plain text stdout is only visible in verbose mode, NOT injected into conversation.
    # The correct format is: {"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"..."}}
    local _ESCAPED
    _ESCAPED=$(escape_for_json "$_REMINDER")
    printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$_ESCAPED"

    exit 0
}

main "$@"
