#!/usr/bin/env bash
# ── gen-code-refs.sh ───────────────────────────────────────────────────────────
# Scans src/ for /// doc comments containing paper labels
# (def:…, lem:…, thm:…, prop:…, sec:…) and generates
# draft/code-refs-gen.tex with a \coderef entry for each label.
#
# Each entry records the code construct, file path, git commit, line range,
# and a hyperlink to the GitHub blob URL.
#
# Usage: ./scripts/gen-code-refs.sh [--check]
#   --check   exit 1 if generated content differs from what's on disk

set -euo pipefail
cd "$(dirname "$0")/.."

GEN_FILE="draft/code-refs-gen.tex"
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
FULL_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "unknown")

# ── GitHub remote detection ────────────────────────────────────────────────────

GITHUB_URL=""
REMOTE=$(git remote get-url origin 2>/dev/null || true)
if [[ "$REMOTE" =~ github\.com[:/]([^/]+)/([^/]+?)(\.git)?$ ]]; then
    OWNER="${BASH_REMATCH[1]}"
    REPO="${BASH_REMATCH[2]%.git}"
    GITHUB_URL="https://github.com/${OWNER}/${REPO}/blob/${FULL_COMMIT}"
fi

# ── Temporary file ────────────────────────────────────────────────────────────

TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT

{
    echo "% Auto-generated by scripts/gen-code-refs.sh — do not edit."
    echo "% Commit: $COMMIT  |  Generated: $(date -u +%Y-%m-%d\ %H:%M:%S\ UTC)"
    echo ""
} > "$TMP"

# ── Associative array for deduplication ───────────────────────────────────────

declare -A seen

# ── Helper ─────────────────────────────────────────────────────────────────────

# process_entry file item_lineno doc_start doc_lines… item_line
process_entry() {
    local file="$1" item_lineno="$2" doc_start="$3"
    shift 3
    local all=("$@")
    local item_line="${all[-1]}"
    unset 'all[-1]'

    local all_text="${all[*]}"
    local labels=()
    while IFS= read -r match; do
        labels+=("$match")
    done < <(echo "$all_text" | grep -oP '`(def|lem|thm|prop|sec):[a-z0-9-]+`' | sed 's/`//g' || true)

    [[ ${#labels[@]} -eq 0 ]] && return

    local construct=""
    local trimmed="${item_line#"${item_line%%[![:space:]]*}"}"

    if [[ "$trimmed" =~ ^(pub\ )?(struct|enum|trait|type|fn|impl|mod)\ +([A-Za-z_][A-Za-z0-9_]*) ]]; then
        construct="${BASH_REMATCH[2]} ${BASH_REMATCH[3]}"
    elif [[ "$trimmed" =~ ^macro_rules! ]]; then
        construct="macro_rules!"
    elif [[ "$trimmed" == //!* ]]; then
        construct="module doc"
    elif [[ -n "$trimmed" ]]; then
        construct="${trimmed:0:50}"
    else
        construct="(module-level)"
    fi

    local lstart=${doc_start:-$item_lineno}
    local lend=$((item_lineno - 1))

    for label in "${labels[@]}"; do
        seen[$label]="${construct}|${file}|${COMMIT}|${lstart}|${lend}"
    done
}

# ── Scan all Rust files ────────────────────────────────────────────────────────

while IFS= read -r -d '' file; do
    rel="${file#./}"
    state="none"
    doc_lines=()
    doc_start=0

    lineno=0
    while IFS= read -r line || [[ -n "$line" ]]; do
        lineno=$((lineno + 1))
        trimmed="${line#"${line%%[![:space:]]*}"}"

        case "$state" in
            none)
                if [[ "$trimmed" == '///'* ]]; then
                    state="in_doc"
                    doc_lines=("$trimmed")
                    doc_start=$lineno
                fi
                ;;
            in_doc)
                if [[ "$trimmed" == '///'* ]]; then
                    doc_lines+=("$trimmed")
                elif [[ -z "$trimmed" ]]; then
                    :
                else
                    process_entry "$rel" "$lineno" "$doc_start" "${doc_lines[@]}" "$trimmed"
                    doc_lines=()
                    state="none"
                fi
                ;;
        esac
    done < "$file"

    if [[ "$state" == "in_doc" && ${#doc_lines[@]} -gt 0 ]]; then
        process_entry "$rel" "$lineno" "$doc_start" "${doc_lines[@]}" ""
    fi

done < <(find src -name '*.rs' -print0 2>/dev/null)

# ── Write sorted entries ──────────────────────────────────────────────────────

count=0
for label in $(printf '%s\n' "${!seen[@]}" | sort); do
    IFS='|' read -r construct file commit lstart lend <<< "${seen[$label]}"
    if [[ -n "$GITHUB_URL" ]]; then
        ghref="${GITHUB_URL}/${file}#L${lstart}-L${lend}"
        echo "\\coderef{${label}}{${construct}}{${file}}{${commit}}{${lstart}--${lend}}{${ghref}}" >> "$TMP"
    else
        echo "\\coderef{${label}}{${construct}}{${file}}{${commit}}{${lstart}--${lend}}{}" >> "$TMP"
    fi
    count=$((count + 1))
done

echo "" >> "$TMP"

# ── Check mode ────────────────────────────────────────────────────────────────

if [[ "${1:-}" == "--check" ]]; then
    if ! diff -q "$GEN_FILE" "$TMP" >/dev/null 2>&1; then
        echo "ERROR: $GEN_FILE is out of date. Run scripts/gen-code-refs.sh." >&2
        diff "$GEN_FILE" "$TMP" >&2 || true
        exit 1
    fi
    echo "OK: $GEN_FILE is up to date."
    exit 0
fi

mv "$TMP" "$GEN_FILE"
echo "Generated $GEN_FILE with $count entries (commit $COMMIT)"
if [[ -n "$GITHUB_URL" ]]; then
    echo "GitHub: $GITHUB_URL"
fi
