#!/usr/bin/env bash
# build-dep-graph.sh — scan .dev/goals/ for **依赖**: lines and produce
# .dev/goals/DEPENDENCY.md + optionally DEPENDENCY.svg (via mmdc).
#
# Usage:
#   scripts/build-dep-graph.sh          # writes .dev/goals/DEPENDENCY.md
#   scripts/build-dep-graph.sh --svg    # also tries to render SVG (needs mmdc)
#
# The script is idempotent: re-running it with the same goal files produces
# the same output. Timestamp in the output reflects the last run time.
#
# Compatibility: bash 3.2+ (macOS default), no associative arrays.

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
GOALS_DIR="$REPO_ROOT/.dev/goals"
OUTPUT_MD="$GOALS_DIR/DEPENDENCY.md"
OUTPUT_SVG="$GOALS_DIR/DEPENDENCY.svg"
WANT_SVG=0
TMPDIR="${TMPDIR:-/tmp}"

if [[ "${1:-}" == "--svg" ]]; then
  WANT_SVG=1
fi

# ---- 1. Key-value store via temp files (bash 3.2 compat) --------------------
# macOS ships bash 3.2 which lacks associative arrays. We use temp files
# keyed by (prefix, sanitized-key) as a flat key-value store.

_sanitize_key() {
  # Replace '/' with '_' so file paths don't create subdirectories.
  echo "$1" | tr '/' '_'
}

_mkpath() {
  local prefix="$1" key
  key=$(_sanitize_key "$2")
  echo "${TMPDIR}/bdg-${prefix}-${key}"
}

_get() {
  local prefix="$1" key="$2"
  local f="$(_mkpath "$prefix" "$key")"
  [[ -f "$f" ]] && cat "$f" || true
}

_set() {
  local prefix="$1" key="$2" value="$3"
  local f="$(_mkpath "$prefix" "$key")"
  echo "$value" > "$f"
}

_append() {
  local prefix="$1" key="$2" value="$3"
  local f="$(_mkpath "$prefix" "$key")"
  echo "$value" >> "$f"
}

_list_keys() {
  local prefix="$1"
  find "$TMPDIR" -maxdepth 1 -name "bdg-${prefix}-*" -print0 2>/dev/null | \
    xargs -0 -n1 basename 2>/dev/null | \
    sed "s/^bdg-${prefix}-//"
}

# ---- 2. Cleanup left-over temp files from previous run ----------------------
rm -f "$TMPDIR"/bdg-title-* "$TMPDIR"/bdg-deps-* "$TMPDIR"/bdg-revdeps-* \
      "$TMPDIR"/bdg-files-* "$TMPDIR"/bdg-mermaid-*

# ---- 3. Parse each goal file ------------------------------------------------
while IFS= read -r goal_file; do
  tag=$(basename "$goal_file" | sed -E 's/^([0-9]+).*/\1/')
  [[ -z "$tag" ]] || [[ ! "$tag" =~ ^[0-9]+$ ]] && continue

  # Title from first heading
  title=$(head -1 "$goal_file" | sed -E 's/^#[[:space:]]*Goal [0-9]+[[:space:]]*[—–-]*[[:space:]]*//' | xargs)
  [[ -z "$title" ]] && title="(no title)"
  _set "title" "$tag" "$title"

  # Dependency line
  dep_line=$(grep -m1 '^\*\*依赖\*\*:' "$goal_file" 2>/dev/null || true)
  if [[ -z "$dep_line" ]]; then
    _set "deps" "$tag" ""
    continue
  fi

  # Check if "无" (none) with no goal numbers after it
  has_none=$(echo "$dep_line" | grep -cE '无|none|None|NONE' || true)
  has_numbers=$(echo "$dep_line" | grep -cE '[0-9]{2,4}' || true)
  if [[ "$has_none" -gt 0 ]] && [[ "$has_numbers" -eq 0 ]]; then
    _set "deps" "$tag" ""
    continue
  fi

  # Extract goal numbers. Goal references can use Chinese commas (、) or
  # Western commas, e.g. "Goal 219、220、221" or "Goal 219, 220, 221".
  # Also handles range patterns like "Goal 229-01 .. 229-NN" and markdown
  # bold markers like "**Goal 229-01**".
  # Strategy: normalize commas to spaces, strip markdown ** markers, then
  # use awk to capture all consecutive goal-number-like tokens after "Goal".
  normalized=$(echo "$dep_line" | sed 's/[、，,]/ /g; s/\*\*//g')
  deps=$(echo "$normalized" | awk '{
    in_goals=0
    for (i=1; i<=NF; i++) {
      if ($i == "Goal") { in_goals=1; continue }
      if (in_goals && $i ~ /^[0-9]+(-[0-9]+)?$/) { print $i; continue }
      if (in_goals && $i !~ /^[0-9]/) { in_goals=0 }
    }
  }' | sed 's/-.*//' | sort -un | tr '\n' ' ' | xargs)
  _set "deps" "$tag" "$deps"
done < <(find "$GOALS_DIR" -maxdepth 1 -name '*.md' ! -name 'DEPENDENCY.md' -print 2>/dev/null | sort)

# ---- 4. Build reverse dependency map ----------------------------------------
for goal in $(_list_keys "deps"); do
  deps=$(_get "deps" "$goal")
  [[ -z "$deps" ]] && continue
  for dep in $deps; do
    _append "revdeps" "$dep" "$goal"
  done
done

# ---- 5. Per-file goal-touch map (heuristic) ----------------------------------
while IFS= read -r goal_file; do
  tag=$(basename "$goal_file" | sed -E 's/^([0-9]+).*/\1/')
  [[ -z "$tag" ]] || [[ ! "$tag" =~ ^[0-9]+$ ]] && continue

  mentioned=$(grep -oE '\b(src|tests|crates)/[a-zA-Z0-9_/.-]+\.rs\b' "$goal_file" 2>/dev/null | sort -u || true)
  for f in $mentioned; do
    f=$(echo "$f" | sed 's|^\./||')
    _append "files" "$f" "$tag"
  done
done < <(find "$GOALS_DIR" -maxdepth 1 -name '*.md' ! -name 'DEPENDENCY.md' -print 2>/dev/null | sort)

# ---- 6. Generate DEPENDENCY.md ----------------------------------------------
TIMESTAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

{
  echo "# Goal Dependency Graph"
  echo ""
  echo "> Auto-generated by \`scripts/build-dep-graph.sh\` (last run: ${TIMESTAMP})."
  echo "> Source: each goal's \`**依赖**: Goal N, M\` line."
  echo ""

  # --- Forward deps (this goal blocks others) ---
  echo "## Forward deps (this goal blocks others)"
  echo ""
  has_forward=0
  for goal in $(_list_keys "revdeps" | sort -n); do
    blockers=$(cat "$(_mkpath "revdeps" "$goal")" 2>/dev/null | sort -nu | tr '\n' ' ' | xargs)
    [[ -z "$blockers" ]] && continue
    title=$(_get "title" "$goal")
    echo "### Goal ${goal} — ${title}"
    echo "- Blocks: ${blockers}"
    echo ""
    has_forward=1
  done
  [[ $has_forward -eq 0 ]] && echo "_No forward dependencies found._" && echo ""

  # --- Reverse deps (this goal is blocked by) ---
  echo "## Reverse deps (this goal is blocked by)"
  echo ""
  has_reverse=0
  for goal in $(_list_keys "deps" | sort -n); do
    deps=$(_get "deps" "$goal")
    [[ -z "$deps" ]] && continue
    title=$(_get "title" "$goal")
    echo "### Goal ${goal} — ${title}"
    echo "- Blocked by: ${deps}"
    echo ""
    has_reverse=1
  done
  [[ $has_reverse -eq 0 ]] && echo "_No reverse dependencies found._" && echo ""

  # --- High-risk file map ---
  echo "## High-risk file map"
  echo ""
  echo "Files mentioned in 2+ goals (≥2 = potential merge conflict risk)."
  echo ""
  echo "| File | Touched by goals |"
  echo "|------|------------------|"
  for file_key in $(_list_keys "files" | sort); do
    touchers=$(cat "$(_mkpath "files" "$file_key")" 2>/dev/null | sort -nu | tr '\n' ' ' | xargs)
    count=$(echo "$touchers" | wc -w | tr -d ' ')
    [[ $count -ge 2 ]] || continue
    echo "| \`${file_key}\` | ${touchers} |"
  done
  echo ""

  # --- Stats ---
  total_goals=$(find "$GOALS_DIR" -maxdepth 1 -name '*.md' ! -name 'DEPENDENCY.md' | wc -l | tr -d ' ')
  with_deps=0
  for goal in $(_list_keys "deps"); do
    deps=$(_get "deps" "$goal")
    [[ -n "$deps" ]] && with_deps=$((with_deps + 1))
  done
  echo "---"
  echo ""
  echo "Stats: ${total_goals} total goals, ${with_deps} with explicit dependencies."
  echo ""

} > "$OUTPUT_MD"

echo "[build-dep-graph] wrote $OUTPUT_MD ($(wc -l < "$OUTPUT_MD" | tr -d ' ') lines)"

# ---- 7. Optional SVG render (via mmdc) --------------------------------------
if [[ $WANT_SVG -eq 1 ]]; then
  if command -v mmdc >/dev/null 2>&1; then
    MMD_FILE="$(_mkpath "mermaid" "mmd")"
    {
      echo "graph TD"
      for goal in $(_list_keys "revdeps" | sort -n); do
        blockers=$(cat "$(_mkpath "revdeps" "$goal")" 2>/dev/null | sort -nu | tr '\n' ' ' | xargs)
        for blocker in $blockers; do
          echo "  G${goal}[\"${goal}\"] --> G${blocker}[\"${blocker}\"]"
        done
      done
      # Standalone goals
      for goal in $(_list_keys "title" | sort -n); do
        has_any=0
        deps=$(_get "deps" "$goal")
        [[ -n "$deps" ]] && has_any=1
        [[ -f "$(_mkpath "revdeps" "$goal")" ]] && has_any=1
        [[ $has_any -eq 0 ]] && echo "  G${goal}[\"${goal}\"]"
      done
    } > "$MMD_FILE"
    mmdc -i "$MMD_FILE" -o "$OUTPUT_SVG" -b transparent 2>/dev/null && \
      echo "[build-dep-graph] SVG rendered to $OUTPUT_SVG" || \
      echo "[build-dep-graph] WARNING: mmdc render failed (SVG not generated)"
    rm -f "$MMD_FILE"
  else
    echo "[build-dep-graph] WARNING: mmdc not found; skipping SVG render."
    echo "[build-dep-graph]   Install: npm install -g @mermaid-js/mermaid-cli"
  fi
fi

# ---- 8. Cleanup temp files --------------------------------------------------
rm -f "$TMPDIR"/bdg-title-* "$TMPDIR"/bdg-deps-* "$TMPDIR"/bdg-revdeps-* \
      "$TMPDIR"/bdg-files-* "$TMPDIR"/bdg-mermaid-*
