#!/usr/bin/env bash
# wiki-lint.sh — Mechanischer Wiki-Health-Check (kein LLM)
# Version: 1.0 (Final 2026-06-17)
# Repo: knowledge-base
# Usage: ./scripts/wiki-lint.sh [repo-root] [output-file]

set -u  # nounset; NICHT -e, weil wir alle Checks durchlaufen wollen

# --- Konfiguration (Schwellwerte) ---
SCRIPT_VERSION="1.0"
STUB_WARN_PCT=10
STUB_FAIL_PCT=25
DIR_WARN_FILES=12
DIR_FAIL_FILES=20
STALE_DAYS=30
EXIT_OK=0
EXIT_ISSUES=1
EXIT_ERROR=2

REPO_ROOT="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
OUTPUT="${2:-$REPO_ROOT/scripts/wiki-lint-report.md}"
TIMESTAMP="$(date -u +"%Y-%m-%d %H:%M:%S UTC")"

cd "$REPO_ROOT" || { echo "FAIL: cannot cd to $REPO_ROOT" >&2; exit 2; }

# --- Zähler ---
total_pages=0
issues=0
status_ok="✅"
status_warn="⚠️"
status_fail="❌"

# --- Ergebnis-Sammler ---
declare -a section_lines
current_section=""

# Helper: Sektion hinzufügen
add_line() { section_lines+=("$1"); }
new_section() {
    current_section="$1"
    section_lines+=("")
    section_lines+=("### $1")
}

# --- Files sammeln ---
# Meta-Files und Tasks-Dir ausschließen
mapfile -t all_wiki < <(find wiki -name "*.md" -type f -not -path "wiki/tasks/*" 2>/dev/null | sort)
total_pages=${#all_wiki[@]}

# ============================================================
# CHECK 1: File-Count per Dir (Trigger für Refactor)
# ============================================================
new_section "File-Count per Dir (Refactor-Trigger)"
declare -A dir_count
while IFS= read -r f; do
    dir=$(dirname "$f")
    dir_count["$dir"]=$(( ${dir_count["$dir"]:-0} + 1 ))
done < <(printf '%s\n' "${all_wiki[@]}")

add_line "| Dir | Files | Trigger |"
add_line "|-----|-------|---------|"
for dir in $(printf '%s\n' "${!dir_count[@]}" | sort); do
    cnt=${dir_count["$dir"]}
    if [ "$cnt" -ge "$DIR_FAIL_FILES" ]; then
        add_line "| $dir | $cnt | ❌ FAIL (refactor) |"
        issues=$((issues+1))
    elif [ "$cnt" -ge "$DIR_WARN_FILES" ]; then
        add_line "| $dir | $cnt | ⚠️ warn (refactor) |"
    else
        add_line "| $dir | $cnt | ✅ |"
    fi
done
add_line ""
add_line "_Schema v1.5 Sub-Categories: \`concepts/hardware\`, \`concepts/agi\`, \`people/\`, \`institutions/\` (alle in Counts oben enthalten)_"

# ============================================================
# CHECK 2: Stubs (<30 Zeilen) + Stub-Quote
# ============================================================
new_section "Stub-Quote (<30 Zeilen)"
stub_count=0
stub_files=()
for f in "${all_wiki[@]}"; do
    lines=$(wc -l < "$f")
    if [ "$lines" -lt 30 ]; then
        # Meta-Files ausschließen
        case "$(basename "$f")" in
            index.md|log.md|ideas.md|tasks.md) continue ;;
        esac
        stub_count=$((stub_count+1))
        stub_files+=("$f ($lines lines)")
    fi
done

if [ "$total_pages" -gt 0 ]; then
    stub_pct=$((stub_count * 100 / total_pages))
else
    stub_pct=0
fi

if [ "$stub_pct" -ge "$STUB_FAIL_PCT" ]; then
    add_line "$status_fail **$stub_count / $total_pages ($stub_pct%)** — threshold $STUB_WARN_PCT%, fail $STUB_FAIL_PCT%"
    issues=$((issues+1))
elif [ "$stub_pct" -ge "$STUB_WARN_PCT" ]; then
    add_line "$status_warn **$stub_count / $total_pages ($stub_pct%)** — threshold $STUB_WARN_PCT%"
else
    add_line "$status_ok **$stub_count / $total_pages ($stub_pct%)**"
fi

if [ "${#stub_files[@]}" -gt 0 ] && [ "$stub_count" -le 15 ]; then
    add_line ""
    add_line "Stub-Files:"
    for s in "${stub_files[@]}"; do add_line "- \`$s\`"; done
fi

# ============================================================
# CHECK 3: Frontmatter fehlt
# ============================================================
new_section "Frontmatter Präsenz"
missing_fm=()
present_fm=0
for f in "${all_wiki[@]}"; do
    # Meta-Files dürfen ohne
    case "$(basename "$f")" in
        index.md|log.md|ideas.md) continue ;;
    esac
    first=$(head -1 "$f")
    if [ "$first" = "---" ]; then
        present_fm=$((present_fm+1))
    else
        missing_fm+=("$f")
    fi
done

if [ "${#missing_fm[@]}" -gt 0 ]; then
    add_line "$status_fail **${#missing_fm[@]} missing, $present_fm present**"
    issues=$((issues+1))
    add_line ""
    add_line "Missing:"
    for m in "${missing_fm[@]}"; do add_line "- \`$m\`"; done
else
    add_line "$status_ok **$present_fm / $total_pages present**"
fi

# ============================================================
# CHECK 4: Broken Cross-Refs
# ============================================================
new_section "Broken Cross-References"
broken_refs=()
# Sammle alle .md-Pfade relativ zum Repo
mapfile -t all_md_rel < <(find . -name "*.md" -not -path "./.git/*" -type f 2>/dev/null | sed 's|^\./||' | sort)

# Grep nach ](path.md) in wiki-Files
for f in "${all_wiki[@]}"; do
    while IFS= read -r match; do
        # match ist z.B. "concepts/llm/foo.md" oder "../other/bar.md"
        target=$(echo "$match" | sed 's|^(\.\./)*||' | sed 's|^wiki/||')
        # Auflösen relativ zur File-Dir
        fdir=$(dirname "$f")
        if [ "$fdir" = "." ]; then
            resolved="$target"
        else
            resolved="$fdir/$target"
        fi
        # Normalize
        resolved=$(realpath -m --relative-to=. "$resolved" 2>/dev/null || echo "$resolved")
        if [ ! -f "$resolved" ] && [ ! -f "wiki/$target" ] && [ ! -f "$target" ]; then
            broken_refs+=("$f → $match")
        fi
    done < <(grep -oE '\]\(([^)]+\.md)\)' "$f" 2>/dev/null | sed 's|](||;s|)||')
done

if [ "${#broken_refs[@]}" -gt 0 ]; then
    add_line "$status_fail **${#broken_refs[@]} broken refs**"
    issues=$((issues+1))
    for b in "${broken_refs[@]}"; do add_line "- \`$b\`"; done
else
    add_line "$status_ok **0 broken**"
fi

# ============================================================
# CHECK 5: Orphan Pages (kein eingehender Link)
# ============================================================
new_section "Orphan Pages (kein eingehender Link)"
orphans=()
for f in "${all_wiki[@]}"; do
    basename=$(basename "$f" .md)
    # Meta-Files auslassen
    case "$basename" in
        index|log|ideas|tasks) continue ;;
    esac
    # Suche nach basename in allen anderen wiki-Files (ohne die Datei selbst)
    if ! grep -rq "$basename" "${all_wiki[@]/$f/}" 2>/dev/null; then
        orphans+=("$f")
    fi
done

if [ "${#orphans[@]}" -gt 0 ]; then
    add_line "$status_warn **${#orphans[@]} orphan pages**"
    for o in "${orphans[@]}"; do add_line "- \`$o\`"; done
else
    add_line "$status_ok **0 orphans**"
fi

# ============================================================
# CHECK 6: Dup-Topic-Cluster (Keyword-Suche)
# ============================================================
new_section "Dup-Topic Cluster (substantielle Vorkommen)"
keywords=("polymarket" "lightning" "iron.condor" "subconscious" "memory" "trading")
declare -A cluster_files
for kw in "${keywords[@]}"; do
    cluster_files["$kw"]=""
done
for f in "${all_wiki[@]}"; do
    case "$(basename "$f")" in
        index.md|log.md|ideas.md|tasks.md) continue ;;
    esac
    for kw in "${keywords[@]}"; do
        count=$(grep -ciE "\\b$kw\\b" "$f" 2>/dev/null)
        count=${count:-0}
        if [ "$count" -ge 3 ]; then
            cluster_files["$kw"]="${cluster_files[$kw]}$f|$count
"
        fi
    done
done
cluster_found=0
for kw in "${keywords[@]}"; do
    files_str="${cluster_files[$kw]}"
    if [ -n "$files_str" ]; then
        file_count=$(echo "$files_str" | grep -c "^[^-]")
        if [ "$file_count" -ge 3 ]; then
            cluster_found=$((cluster_found+1))
            add_line "$status_warn **$kw**: $file_count Files (≥3 Vorkommen je File)"
            while IFS='|' read -r fpath fcount; do
                [ -n "$fpath" ] && add_line "  - \`$fpath\` ($fcount Treffer)"
            done <<< "$files_str"
        fi
    fi
done
if [ "$cluster_found" -eq 0 ]; then
    add_line "$status_ok **keine signifikanten Cluster** (Schwelle: ≥3 Vorkommen in ≥3 Files)"
fi

# ============================================================
# CHECK 7: Stale Wiki (mtime > 30d ohne index-Update)
# ============================================================
new_section "Stale Pages (mtime > 30d)"
stale=()
thirty_days_ago=$(date -d "30 days ago" +%s 2>/dev/null || date -v-30d +%s 2>/dev/null || echo 0)
if [ "$thirty_days_ago" -gt 0 ]; then
    while IFS= read -r f; do
        mtime=$(stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null || echo 0)
        if [ "$mtime" -gt 0 ] && [ "$mtime" -lt "$thirty_days_ago" ]; then
            stale+=("$f")
        fi
    done < <(printf '%s\n' "${all_wiki[@]}")
fi

if [ "${#stale[@]}" -gt 0 ]; then
    add_line "$status_warn **${#stale[@]} stale pages** (>30d)"
    for s in "${stale[@]:0:10}"; do add_line "- \`$s\`"; done
    if [ "${#stale[@]}" -gt 10 ]; then add_line "- ... und $((${#stale[@]}-10)) weitere"; fi
else
    add_line "$status_ok **0 stale**"
fi

# ============================================================
# CHECK 8: Uncovered Raw (raw/-Files ohne Wiki-Cross-Ref)
# ============================================================
new_section "Uncovered Raw Sources"
mapfile -t all_raw < <(find raw -name "*.md" -type f 2>/dev/null | sort)
uncovered=()
for r in "${all_raw[@]}"; do
    rbase=$(basename "$r")
    # Suche in Wiki nach dem Raw-Basename (ohne .md) oder nach dem Slug-Teil
    slug=$(echo "$rbase" | sed 's/\.md$//' | sed 's/^[0-9-]*_//')
    if ! grep -rq "$slug" wiki/ 2>/dev/null; then
        uncovered+=("$r (slug: $slug)")
    fi
done

if [ "${#uncovered[@]}" -gt 0 ]; then
    add_line "$status_warn **${#uncovered[@]} uncovered raw**"
    for u in "${uncovered[@]}"; do add_line "- \`$u\`"; done
else
    add_line "$status_ok **${#all_raw[@]} raw, all covered**"
fi

# ============================================================
# Zusammenfassung + Empfehlung
# ============================================================
summary_section="## Summary"
section_lines+=("")
section_lines+=("$summary_section")
section_lines+=("")
section_lines+=("- Total Wiki-Pages: $total_pages")
section_lines+=("- Issues: $issues")
section_lines+=("")
if [ "$issues" -gt 0 ]; then
    section_lines+=("→ **Action needed**: subagent spawn for fixes. See '## Recommended Subagent Spawns'.")
else
    section_lines+=("→ Wiki is clean. No subagent spawn needed.")
fi

# ============================================================
# Empfohlene Subagent-Spawns (heuristisch)
# ============================================================
section_lines+=("")
section_lines+=("## Recommended Subagent Spawns")
section_lines+=("")

# Heuristik: Welche Subagents sind nötig?
if [ "${#missing_fm[@]}" -gt 0 ]; then
    section_lines+=("- **frontmatter-migration** — ${#missing_fm[@]} files missing frontmatter")
fi
if [ "${#orphans[@]}" -gt 0 ]; then
    section_lines+=("- **orphan-resolver** — ${#orphans[@]} orphan pages (link or delete)")
fi
if [ "${#stub_files[@]}" -gt 0 ] && [ "$stub_pct" -ge 10 ]; then
    section_lines+=("- **stub-killer** — $stub_count stubs ($stub_pct%, threshold 10%)")
fi
# Dup-Topic-Cluster: bei polymarket/lightning/trading ≥3 hits
dup_warn=$(printf '%s\n' "${section_lines[@]}" | grep -c "^\*\*\(polymarket\|lightning\|iron\.condor\|trading\)\*\*" 2>/dev/null) || dup_warn=0
dup_warn=${dup_warn:-0}
if [ "$dup_warn" -gt 0 ]; then
    section_lines+=("- **hub-page-creator** — dup-topic clusters detected")
fi
# Tools-Dir ≥12
if [ "${dir_count[wiki/tools]:-0}" -ge 12 ]; then
    section_lines+=("- **tools-refactor** — wiki/tools/ hat ${dir_count[wiki/tools]} files")
fi
if [ "${#uncovered[@]}" -gt 0 ]; then
    section_lines+=("- **raw-coverage-check** — ${#uncovered[@]} raw files ohne Wiki-Cross-Ref")
fi

if [ "$issues" -eq 0 ] && [ "${#orphans[@]}" -eq 0 ] && [ "$stub_pct" -lt 10 ]; then
    section_lines+=("(keine — Wiki sauber)")
fi

# ============================================================
# Output schreiben
# ============================================================
{
    echo "# Wiki Lint Report"
    echo ""
    echo "*Generated: $TIMESTAMP*"
    echo "*Repo: $REPO_ROOT*"
    echo "*Script: scripts/wiki-lint.sh v$SCRIPT_VERSION*"
    echo ""
    printf '%s\n' "${section_lines[@]}"
} > "$OUTPUT"

# Console-Kurzfassung
echo "Wiki Lint — $TIMESTAMP"
echo "Total: $total_pages | Issues: $issues"
echo "Report: $OUTPUT"

# Exit-Code
if [ "$issues" -gt 0 ]; then
    exit "$EXIT_ISSUES"
else
    exit "$EXIT_OK"
fi
