#!/usr/bin/env bash
# aTool - hooks/session-start
# Dynamic context generator: injects skill catalog, project context, and doc-sync reminders
# Works with Claude Code, Cursor, and other IDEs

set -euo pipefail

# Ensure we always exit cleanly — hook failures should not block the IDE
trap 'exit 0' EXIT

# Locate aTool installation
if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" ]]; then
    SCRIPT_DIR="$CLAUDE_PLUGIN_ROOT"
elif [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
    SCRIPT_DIR="$CURSOR_PLUGIN_ROOT"
else
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
fi

# Detect which IDE is running this hook
HOOK_IDE="claude"
if [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
    HOOK_IDE="cursor"
fi

# Read version, stripping potential CRLF from Windows
ATOOL_VERSION="${ATOOL_VERSION:-$(cat "$SCRIPT_DIR/../VERSION" 2>/dev/null || cat "$SCRIPT_DIR/VERSION" 2>/dev/null || echo "0.1.0")}"
ATOOL_VERSION="${ATOOL_VERSION%$'\r'}"

# Source common functions (scan_skills_catalog, stack_to_conventions_skill, count_stale_docs)
# Search order: PLUGIN_ROOT/lib/ → sibling lib/ → installed IDE lib/.
#
# SECURITY (v1.10.14, P1-SEC-2): the search path MUST NOT include any
# CWD-relative directory. Earlier versions iterated `${PWD}/.claude/lib`,
# which let a malicious repo ship its own `.claude/lib/common.sh`; if a user
# opened that repo and the hook fired (or was run standalone), the
# attacker's code was sourced under the user's account. The trusted set is
# now: paths anchored to the hook's own script dir (where atool installs
# its lib alongside) and paths under $HOME (installed by atool). PWD is
# explicitly excluded.
# shellcheck source=../lib/common.sh
_LIB_FOUND=false
for _lib_dir in "${SCRIPT_DIR}/lib" "${SCRIPT_DIR}/../lib" "$HOME/.claude/lib" "$HOME/.cursor/lib"; do
    if [[ -f "${_lib_dir}/common.sh" ]]; then
        source "${_lib_dir}/common.sh"
        _LIB_FOUND=true
        break
    fi
done

# Source detect-stack.sh for stack detection
# shellcheck source=../lib/detect-stack.sh
if [[ -f "${_lib_dir:-}/detect-stack.sh" ]]; then
    source "${_lib_dir}/detect-stack.sh"
fi
unset _lib_dir _LIB_FOUND

# Escape string for JSON embedding (same approach as Superpowers)
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"
}

# ═══════════════════════════════════════════════════════════════════════════
# 1. Inject using-superpowers skill content
# ═══════════════════════════════════════════════════════════════════════════

INJECTION=""
SUPERPOWERS_SKILLS=""
if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" ]]; then
    SUPERPOWERS_SKILLS="$CLAUDE_PLUGIN_ROOT/skills/using-superpowers"
elif [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
    SUPERPOWERS_SKILLS="$CURSOR_PLUGIN_ROOT/skills/using-superpowers"
fi

# Fallback: scan IDE-specific config dirs
if [[ -z "$SUPERPOWERS_SKILLS" || ! -d "$SUPERPOWERS_SKILLS" ]]; then
    if [[ "$HOOK_IDE" == "cursor" ]] && declare -f get_cursor_config_dir &>/dev/null; then
        _sp_config_dir=$(get_cursor_config_dir 2>/dev/null || echo "")
        if [[ -d "$_sp_config_dir/skills/using-superpowers" ]]; then
            SUPERPOWERS_SKILLS="$_sp_config_dir/skills/using-superpowers"
        fi
    fi
fi
if [[ -z "$SUPERPOWERS_SKILLS" || ! -d "$SUPERPOWERS_SKILLS" ]]; then
    if [[ -d "$HOME/.claude/skills/using-superpowers" ]]; then
        SUPERPOWERS_SKILLS="$HOME/.claude/skills/using-superpowers"
    elif [[ -d "$HOME/.atool/cache/superpowers/skills/using-superpowers" ]]; then
        SUPERPOWERS_SKILLS="$HOME/.atool/cache/superpowers/skills/using-superpowers"
    fi
fi

if [[ -n "$SUPERPOWERS_SKILLS" && -d "$SUPERPOWERS_SKILLS" ]]; then
    SKILL_FILE=""
    if [[ -f "$SUPERPOWERS_SKILLS/SKILL.md" ]]; then
        SKILL_FILE="$SUPERPOWERS_SKILLS/SKILL.md"
    elif [[ -f "$SUPERPOWERS_SKILLS/skill.md" ]]; then
        SKILL_FILE="$SUPERPOWERS_SKILLS/skill.md"
    fi

    if [[ -n "$SKILL_FILE" ]]; then
        INJECTION+=$(cat "$SKILL_FILE")
        INJECTION+=$'\n\n'
    fi
fi

# ═══════════════════════════════════════════════════════════════════════════
# 2. Scan installed skills and generate catalog
# ═══════════════════════════════════════════════════════════════════════════

# Use IDE-appropriate skills directory
SKILLS_DIR="$HOME/.claude/skills"
if [[ "$HOOK_IDE" == "cursor" ]] && declare -f get_cursor_config_dir &>/dev/null; then
    _cursor_skills=$(get_cursor_config_dir 2>/dev/null || echo "$HOME/.cursor")
    _cursor_skills="$_cursor_skills/skills"
    if [[ -d "$_cursor_skills" ]]; then
        SKILLS_DIR="$_cursor_skills"
    fi
fi
unset _cursor_skills _sp_config_dir 2>/dev/null || true

CATALOG=""
if declare -f scan_skills_catalog &>/dev/null; then
    CATALOG=$(scan_skills_catalog "$SKILLS_DIR")
fi

# ═══════════════════════════════════════════════════════════════════════════
# 3. Detect project context (only when in a project directory)
# ═══════════════════════════════════════════════════════════════════════════

PROJECT_CONTEXT=""
DOC_SYNC=""
PROJECT_DIR="${PWD:-}"

# Only inject project context if we're in a real project (has .git or signal files)
IS_PROJECT=false
if [[ -d "$PROJECT_DIR/.git" ]] || [[ -f "$PROJECT_DIR/pom.xml" ]] || \
   [[ -f "$PROJECT_DIR/package.json" ]] || [[ -f "$PROJECT_DIR/Cargo.toml" ]] || \
   [[ -f "$PROJECT_DIR/go.mod" ]] || [[ -f "$PROJECT_DIR/pyproject.toml" ]] || \
   [[ -f "$PROJECT_DIR/requirements.txt" ]] || [[ -f "$PROJECT_DIR/pubspec.yaml" ]] || \
   [[ -f "$PROJECT_DIR/build.gradle" ]] || [[ -f "$PROJECT_DIR/build.gradle.kts" ]] || \
   [[ -f "$PROJECT_DIR/dbt_project.yml" ]] || [[ -f "$PROJECT_DIR/spark-defaults.conf" ]]; then
    IS_PROJECT=true
fi

if $IS_PROJECT && declare -f detect_stack &>/dev/null; then
    STACK=$(detect_stack "$PROJECT_DIR")
    STACK_DESC=""
    if declare -f get_stack_description &>/dev/null; then
        STACK_DESC=$(get_stack_description "$STACK")
    fi

    CONVENTIONS_SKILL=""
    if declare -f stack_to_conventions_skill &>/dev/null; then
        CONVENTIONS_SKILL=$(stack_to_conventions_skill "$STACK")
    fi

    PROJECT_CONTEXT="<ATOOL-PROJECT-CONTEXT>"
    PROJECT_CONTEXT+=$'\n'
    PROJECT_CONTEXT+="Current project: ${STACK_DESC} (${STACK})"
    PROJECT_CONTEXT+=$'\n'
    if [[ -n "$CONVENTIONS_SKILL" ]]; then
        PROJECT_CONTEXT+="Primary conventions: /${CONVENTIONS_SKILL}"
        PROJECT_CONTEXT+=$'\n'
    fi

    # Check doc freshness against ALL documentation files
    STALE_COUNT=0
    _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
    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 declare -f count_stale_docs &>/dev/null && [[ ${#_DOC_FILES[@]} -gt 0 ]]; then
        STALE_COUNT=$(count_stale_docs "$PROJECT_DIR" "${_DOC_FILES[@]}")
    fi
    if [[ "$STALE_COUNT" -gt 0 ]]; then
        PROJECT_CONTEXT+="Doc status: STALE — ${STALE_COUNT} source files newer than docs (${#_DOC_FILES[@]} docs checked)"
        PROJECT_CONTEXT+=$'\n'
    else
        PROJECT_CONTEXT+="Doc status: All docs up to date (${#_DOC_FILES[@]} docs checked)"
        PROJECT_CONTEXT+=$'\n'
    fi

    # Check if UI project is missing UI_STYLE.md
    IS_UI_PROJECT=false
    case "$STACK" in
        web-*|android-*|mobile-*|harmony|rust-tauri) IS_UI_PROJECT=true ;;
    esac
    if $IS_UI_PROJECT && [[ ! -f "$PROJECT_DIR/UI_STYLE.md" ]]; then
        PROJECT_CONTEXT+="UI spec: MISSING — This is a UI project but UI_STYLE.md not found. Consider running /ui-ux-pro to establish design guidelines."
        PROJECT_CONTEXT+=$'\n'
    fi
    if $IS_UI_PROJECT && [[ ! -f "$PROJECT_DIR/DESIGN.md" ]]; then
        PROJECT_CONTEXT+="Design system: MISSING — DESIGN.md not found (Google Stitch protocol). Run /ui-ux-pro to generate a design system manifest."
        PROJECT_CONTEXT+=$'\n'
    fi

    # Data Warehouse project context
    if [[ "$STACK" == "data-warehouse" ]]; then
        PROJECT_CONTEXT+="Data stack: Data Warehouse detected. Available skills:"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /data-conventions — DW naming, layering, SQL standards"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /data-warehouse-modeling — Dimensional modeling, ER design"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /sql-development — SQL coding standards and patterns"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /sql-optimization — Query performance tuning"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /data-quality — Data quality checks and rules"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /data-code-review — SQL/DW code review"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /scheduling-design — Airflow DAG scheduling"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /requirement-analysis — Data requirement analysis"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /test-case-generator — S2T/T2S test cases"
        PROJECT_CONTEXT+=$'\n'
        PROJECT_CONTEXT+="  /deployment — Data deployment and release"
        PROJECT_CONTEXT+=$'\n'

        # Check DW-specific docs
        if [[ ! -f "$PROJECT_DIR/COMPONENT.md" ]]; then
            PROJECT_CONTEXT+="DW docs: MISSING — COMPONENT.md not found. Run /data-warehouse-modeling to document layer architecture."
            PROJECT_CONTEXT+=$'\n'
        fi
    fi

    # Check analysis status
    if [[ -d "$PROJECT_DIR/.atool-docs" ]]; then
        PROJECT_CONTEXT+="Analysis status: Project analyzed (see .atool-docs/)"
        PROJECT_CONTEXT+=$'\n'
    else
        PROJECT_CONTEXT+="Analysis status: Not yet analyzed - consider running /project-analyze"
        PROJECT_CONTEXT+=$'\n'
    fi

    PROJECT_CONTEXT+="</ATOOL-PROJECT-CONTEXT>"

    # Hard rules — non-bypassable gates extracted from skills
    # These apply in ALL modes: normal, Plan Mode, etc.
    DOC_SYNC="<ATOOL-HARD-RULES>"
    DOC_SYNC+=$'\n'
    DOC_SYNC+="Five NON-NEGOTIABLE rules that apply in ALL modes (Plan Mode, normal mode, etc.):"
    DOC_SYNC+=$'\n\n'
    DOC_SYNC+="1. **Doc Sync Gate**: After modifying any source file, update affected docs (README.md, COMPONENT.md, UI_STYLE.md, DESIGN.md, docs/) BEFORE claiming done. If docs stale -> task NOT done."
    DOC_SYNC+=$'\n'
    DOC_SYNC+="2. **Verification Gate**: Before claiming complete: (a) tests pass, (b) docs fresh, (c) no critical issues. Any fail -> task NOT done. Invoke /verification-before-completion."
    DOC_SYNC+=$'\n'
    DOC_SYNC+="3. **Root Cause Gate**: When debugging, identify root cause BEFORE writing fix. Never guess-fix. See /systematic-debugging."
    DOC_SYNC+=$'\n'
    DOC_SYNC+="4. **Pre-Doc Gate**: Before coding new features, required docs must exist — PRD in docs/300-requirements/, architecture in docs/400-architecture/, UI design in docs/500-design/. Missing = implementation MUST NOT start. Create via /requirements-writer, /software-architecture, /ui-ux-pro."
    DOC_SYNC+=$'\n'
    DOC_SYNC+="5. **TDD Gate**: New features/behavior changes -> write failing test FIRST, then minimal code to pass. No test = no implementation. See /test-driven-development."
    DOC_SYNC+=$'\n\n'
    DOC_SYNC+="When writing plans (any mode): MUST include a final Documentation Sync task. Plans without it are INCOMPLETE."
    DOC_SYNC+=$'\n'
    DOC_SYNC+="</ATOOL-HARD-RULES>"
fi

# ═══════════════════════════════════════════════════════════════════════════
# 4. Assemble and output
# ═══════════════════════════════════════════════════════════════════════════

INJECTION+="<system-reminder>"
INJECTION+=$'\n'
INJECTION+="SessionStart: aTool v${ATOOL_VERSION} active. Skills, hooks, and MCP servers configured."
INJECTION+=$'\n'
INJECTION+="</system-reminder>"
INJECTION+=$'\n\n'
INJECTION+="<EXTREMELY_IMPORTANT>"
INJECTION+=$'\n'
INJECTION+="<ATOOL-SESSION>"
INJECTION+=$'\n'
INJECTION+="aTool v${ATOOL_VERSION} active."
INJECTION+=$'\n\n'

if [[ -n "$CATALOG" ]]; then
    INJECTION+="<ATOOL-SKILL-CATALOG>"
    INJECTION+=$'\n'
    INJECTION+="$CATALOG"
    INJECTION+=$'\n'
    INJECTION+="Invoke skills with: /{skill-name}"
    INJECTION+=$'\n'
    INJECTION+="</ATOOL-SKILL-CATALOG>"
    INJECTION+=$'\n\n'
fi

if [[ -n "$PROJECT_CONTEXT" ]]; then
    INJECTION+="$PROJECT_CONTEXT"
    INJECTION+=$'\n\n'
fi

if [[ -n "$DOC_SYNC" ]]; then
    INJECTION+="$DOC_SYNC"
    INJECTION+=$'\n\n'
fi

# Only inject clarify rule if in a code project (detected by presence of common project files)
if [[ -f "package.json" || -f "pom.xml" || -f "go.mod" || -f "Cargo.toml" || -f "requirements.txt" || -f "build.gradle" || -f ".gradle" || -f "setup.py" || -f "Gemfile" ]]; then
    INJECTION+="<ATOOL-CLARIFY-RULE>"
    INJECTION+=$'\n'
    INJECTION+="Before implementing ANY feature, first classify task complexity:"
    INJECTION+=$'\n'
    INJECTION+="Tier 0 (direct): single-value change, typo, format fix -> just do it"
    INJECTION+=$'\n'
    INJECTION+="Tier 1 (1-2 questions): small change with ambiguity -> ask key questions"
    INJECTION+=$'\n'
    INJECTION+="Tier 2 (3-5 questions): new feature, medium complexity -> structured clarification"
    INJECTION+=$'\n'
    INJECTION+="Tier 3: architecture-level, vague requirements -> delegate to /brainstorming"
    INJECTION+=$'\n'
    INJECTION+="Rules: Read project files BEFORE asking (CLAUDE.md, package.json, source code). Never assume installed libs or existing patterns. Ask context-aware questions, not obvious ones."
    INJECTION+=$'\n'
    INJECTION+="Exit: If user says '别问了'/'直接做'/'just do it'/'stop asking' -> skip all questions, Tier 0."
    INJECTION+=$'\n'
    INJECTION+="See /clarify-before-build for full methodology."
    INJECTION+=$'\n'
    INJECTION+="</ATOOL-CLARIFY-RULE>"
    INJECTION+=$'\n'
fi

# Inject language rule (always, for all projects)
INJECTION+="<ATOOL-LANGUAGE-RULE>"
INJECTION+=$'\n'
INJECTION+="语言要求 (Language Requirement):"
INJECTION+=$'\n'
INJECTION+="- 项目文档 (docs/, README, 架构指南): 必须中文"
INJECTION+=$'\n'
INJECTION+="- SKILL.md: 保持英文（AI 指令文件）"
INJECTION+=$'\n'
INJECTION+="- Commit message: 推荐中文"
INJECTION+=$'\n'
INJECTION+="- 代码注释: 可中文或英文（跟随代码风格）"
INJECTION+=$'\n'
INJECTION+="如果误用英文写项目文档，请用 /doc-coauthoring 重写为中文。"
INJECTION+=$'\n'
INJECTION+="</ATOOL-LANGUAGE-RULE>"
INJECTION+=$'\n'

# v1.10.23 — opt-in update notifier. When ATOOL_UPDATE_NOTIFIER=1 and the 24h
# cache shows an available update, inject a one-liner so the model can mention
# it. Default OFF so this hook adds zero noise unless the user opts in.
# Reads ~/.atool/cache/update-check.json populated by `atool check-updates`.
if [[ "${ATOOL_UPDATE_NOTIFIER:-0}" == "1" ]]; then
    _update_cache="$HOME/.atool/cache/update-check.json"
    if [[ -f "$_update_cache" ]] && command -v jq >/dev/null 2>&1; then
        # Resolve installed version. Prefer ATOOL_ROOT/VERSION (set by install.sh
        # when this hook is sourced), fall back to walking up from $BASH_SOURCE.
        _hook_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
        _installed=""
        for _candidate in "${ATOOL_ROOT:-$_hook_dir/..}/VERSION" "$_hook_dir/../VERSION"; do
            if [[ -f "$_candidate" ]]; then
                _installed=$(tr -d '\n' < "$_candidate")
                break
            fi
        done
        _latest=$(jq -r '.latest // empty' "$_update_cache" 2>/dev/null || true)
        if [[ -n "$_latest" && -n "$_installed" && "$_latest" != "$_installed" ]]; then
            INJECTION+=$'\n'
            INJECTION+="<ATOOL-UPDATE-AVAILABLE>"
            INJECTION+=$'\n'
            INJECTION+="aTool update available: ${_installed} → ${_latest}. Run: atool update"
            INJECTION+=$'\n'
            INJECTION+="(Cached check; refresh with: atool check-updates --force)"
            INJECTION+=$'\n'
            INJECTION+="</ATOOL-UPDATE-AVAILABLE>"
            INJECTION+=$'\n'
        fi
    fi
fi

INJECTION+="</ATOOL-SESSION>"
INJECTION+=$'\n'
INJECTION+="</EXTREMELY_IMPORTANT>"

# Output in the format expected by the running IDE.
# Claude Code reads BOTH additional_context and hookSpecificOutput without deduplication,
# so we must emit only the appropriate field to avoid double injection.
if [[ "$HOOK_IDE" == "cursor" ]]; then
    INJECTION_ESCAPED=$(escape_for_json "$INJECTION")
    printf '{\n  "additional_context": "%s"\n}\n' "$INJECTION_ESCAPED"
else
    # Claude Code: plain text output (hook framework wraps it automatically)
    printf '%s' "$INJECTION"
fi

exit 0
