#!/bin/bash
# generate-modules.sh — regenerate all sentinel regions from modules.conf
#
# Usage:
#   ./generate-modules.sh            — apply changes in-place
#   ./generate-modules.sh --dry-run  — print a unified diff, make no changes
#
# Sentinel format in target files:
#   bash/sh:  # BEGIN GENERATED MODULES: <tag>  /  # END GENERATED MODULES: <tag>
#   markdown: <!-- BEGIN GENERATED MODULES: <tag> --> / <!-- END GENERATED MODULES: <tag> -->
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SETUP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
CONF="$SETUP_DIR/modules.conf"
TUI="$SETUP_DIR/tui-install.sh"
AF="$SCRIPT_DIR/generate-answerfile.sh"   # sibling in setup/tools/
DOCS="$SETUP_DIR/../docs/md/modules.md"

DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true

# ── Parse modules.conf ────────────────────────────────────────────────────────
declare -a M_IDS M_CATS M_DESCS M_DEFAULTS M_EXCLUDES
skipped=()

while IFS='|' read -r id cat desc def excl; do
    # Strip leading/trailing whitespace from each field
    id="${id#"${id%%[![:space:]]*}"}"; id="${id%"${id##*[![:space:]]}"}"
    [[ "$id" =~ ^# || -z "$id" ]] && continue
    M_IDS+=("$id")
    M_CATS+=("$cat")
    M_DESCS+=("$desc")
    M_DEFAULTS+=("${def:-off}")
    M_EXCLUDES+=("${excl:-}")
done < "$CONF"

# Identify incomplete entries (missing category or description) — skip them.
declare -a ACTIVE_IDS
for i in "${!M_IDS[@]}"; do
    if [[ -z "${M_CATS[$i]}" || -z "${M_DESCS[$i]}" ]]; then
        skipped+=("${M_IDS[$i]}")
    else
        ACTIVE_IDS+=("$i")
    fi
done

if (( ${#skipped[@]} > 0 )); then
    printf '[warn] skipping %d stub(s) with empty category/description:\n' "${#skipped[@]}" >&2
    printf '         %s\n' "${skipped[@]}" >&2
fi

# ── Build generated sections ──────────────────────────────────────────────────

# -- module-counters (tui-install.sh: count_steps function body) --------------
gen_counters=""
for i in "${ACTIVE_IDS[@]}"; do
    id="${M_IDS[$i]}"
    gen_counters+="    [[ \" \$a \" == *\" ${id} \"* ]] && TOTAL=\$(( TOTAL + 1 ))\n"
done

# -- module-checklist (tui-install.sh and generate-answerfile.sh) -------------
# Builds the full SELECTED_APPS=$(ui_checklist ...) call including open/close.
# ui_checklist (defined in tui-install.sh) is a plain-bash replacement for the
# `dialog --checklist` widget; it takes the same tag/desc/state triplets.
build_checklist_tui() {
    local out
    out='    SELECTED_APPS=$(ui_checklist " Applications " "Optional applications — installed after base components:" \\\n'
    for i in "${ACTIVE_IDS[@]}"; do
        local id="${M_IDS[$i]}" desc="${M_DESCS[$i]}" def="${M_DEFAULTS[$i]}"
        local padded
        padded=$(printf '%-20s' "$id")
        out+="        \"${id}\"  \"${padded}  ${desc}\"  ${def} \\\\\n"
    done
    out+='        ) || SELECTED_APPS=""\n'
    printf '%s' "$out"
}

build_checklist_af() {
    local count="${#ACTIVE_IDS[@]}"
    local out
    out="    AF_APPS=\$(dialog --backtitle \"\$BACKTITLE\" \\\\\n"
    out+="        --title \" Applications \" \\\\\n"
    out+="        --checklist \"Optional applications — installed after base components:\" 40 76 ${count} \\\\\n"
    for i in "${ACTIVE_IDS[@]}"; do
        local id="${M_IDS[$i]}" desc="${M_DESCS[$i]}" def="${M_DEFAULTS[$i]}"
        local padded
        padded=$(printf '%-20s' "$id")
        out+="        \"${id}\"  \"${padded}  ${desc}\"  ${def} \\\\\n"
    done
    out+='        3>&1 1>&2 2>&3) || AF_APPS=""\n'
    printf '%s' "$out"
}

gen_checklist_tui=$(build_checklist_tui)
gen_checklist_af=$(build_checklist_af)

# -- module-summary (tui-install.sh: confirmation dialog) ---------------------
gen_summary=""
for i in "${ACTIVE_IDS[@]}"; do
    id="${M_IDS[$i]}"
    gen_summary+="        [[ \" \$SELECTED_APPS \" == *\" ${id} \"* ]] && SUMMARY+=\"  ✦ ${id}\\\\n\"\n"
done

# -- module-conflicts (tui-install.sh: before dispatch) -----------------------
gen_conflicts=""
for i in "${ACTIVE_IDS[@]}"; do
    id="${M_IDS[$i]}"
    excl="${M_EXCLUDES[$i]}"
    [[ -z "$excl" ]] && continue
    IFS=',' read -ra excl_list <<< "$excl"
    for eid in "${excl_list[@]}"; do
        eid="${eid#"${eid%%[![:space:]]*}"}"; eid="${eid%"${eid##*[![:space:]]}"}"
        gen_conflicts+="if [[ \" \$SELECTED_APPS \" == *\" ${id} \"* && \" \$SELECTED_APPS \" == *\" ${eid} \"* ]]; then\n"
        gen_conflicts+="    warn \"'${id}' and '${eid}' are mutually exclusive — deselecting '${eid}'\"\n"
        gen_conflicts+="    SELECTED_APPS=\" \$SELECTED_APPS \"\n"
        gen_conflicts+="    SELECTED_APPS=\"\${SELECTED_APPS/ ${eid} / }\"\n"
        gen_conflicts+="    SELECTED_APPS=\"\${SELECTED_APPS# }\"\n"
        gen_conflicts+="    SELECTED_APPS=\"\${SELECTED_APPS% }\"\n"
        gen_conflicts+="fi\n"
    done
done

# -- module-dispatch (tui-install.sh: installation section) -------------------
gen_dispatch=""
for i in "${ACTIVE_IDS[@]}"; do
    id="${M_IDS[$i]}"
    gen_dispatch+="[[ \" \$SELECTED_APPS \" == *\" ${id} \"* ]] && run_module \"${id}\"  \"\$APPS/${id}.sh\"\n"
done

# -- per-category doc tables (docs/md/modules.md) -----------------------------
declare -A gen_docs_cat
for i in "${ACTIVE_IDS[@]}"; do
    id="${M_IDS[$i]}"
    cat="${M_CATS[$i]}"
    desc="${M_DESCS[$i]}"
    gen_docs_cat[$cat]+="| \`${id}\` | ${desc} |\n"
done

build_doc_table() {
    local cat="$1"
    local rows="${gen_docs_cat[$cat]:-}"
    if [[ -z "$rows" ]]; then
        printf ''
        return
    fi
    printf '| ID | Description |\n|----|-------------|\n'
    printf '%b' "$rows"
}

# ── Splice helper (uses python3 for safe multiline replacement) ───────────────
splice() {
    local file="$1" tag="$2" content="$3" style="${4:-bash}"
    local tmpfile
    tmpfile=$(mktemp)
    printf '%b' "$content" > "$tmpfile"

    if $DRY_RUN; then
        python3 - "$file" "$tag" "$tmpfile" "$style" "dry" <<'PYEOF'
import sys, re, difflib
filepath, tag, tmpfile, style, _ = sys.argv[1:]
with open(tmpfile) as f:
    new_content = f.read()
if style == 'md':
    begin = f'<!-- BEGIN GENERATED MODULES: {tag} -->'
    end   = f'<!-- END GENERATED MODULES: {tag} -->'
else:
    begin = f'# BEGIN GENERATED MODULES: {tag}'
    end   = f'# END GENERATED MODULES: {tag}'
with open(filepath) as f:
    original = f.read()
pattern = re.compile(re.escape(begin) + r'\n.*?' + re.escape(end), re.DOTALL)
if not pattern.search(original):
    print(f'ERROR: sentinel not found in {filepath}: {begin}', file=sys.stderr)
    sys.exit(1)
replacement = begin + '\n' + new_content + end
updated = pattern.sub(lambda m: replacement, original)
if updated == original:
    print(f'  (no change)  {filepath}  [{tag}]', file=sys.stderr)
    sys.exit(0)
diff = difflib.unified_diff(
    original.splitlines(keepends=True),
    updated.splitlines(keepends=True),
    fromfile=f'{filepath}',
    tofile=f'{filepath} (generated)',
)
sys.stdout.writelines(diff)
PYEOF
    else
        python3 - "$file" "$tag" "$tmpfile" "$style" <<'PYEOF'
import sys, re
filepath, tag, tmpfile, style = sys.argv[1:]
with open(tmpfile) as f:
    new_content = f.read()
if style == 'md':
    begin = f'<!-- BEGIN GENERATED MODULES: {tag} -->'
    end   = f'<!-- END GENERATED MODULES: {tag} -->'
else:
    begin = f'# BEGIN GENERATED MODULES: {tag}'
    end   = f'# END GENERATED MODULES: {tag}'
with open(filepath) as f:
    original = f.read()
pattern = re.compile(re.escape(begin) + r'\n.*?' + re.escape(end), re.DOTALL)
if not pattern.search(original):
    print(f'ERROR: sentinel not found in {filepath}: {begin}', file=sys.stderr)
    sys.exit(1)
replacement = begin + '\n' + new_content + end
updated = pattern.sub(lambda m: replacement, original)
if updated == original:
    print(f'  (no change)  {filepath}  [{tag}]', file=sys.stderr)
    sys.exit(0)
with open(filepath, 'w') as f:
    f.write(updated)
print(f'  (updated)    {filepath}  [{tag}]', file=sys.stderr)
PYEOF
    fi
    rm -f "$tmpfile"
}

# ── Apply all regions ─────────────────────────────────────────────────────────
echo "==> tui-install.sh"
splice "$TUI" "module-counters"  "$gen_counters"
splice "$TUI" "module-checklist" "$gen_checklist_tui"
splice "$TUI" "module-summary"   "$gen_summary"
splice "$TUI" "module-conflicts" "$gen_conflicts"
splice "$TUI" "module-dispatch"  "$gen_dispatch"

echo "==> generate-answerfile.sh"
splice "$AF" "module-checklist" "$gen_checklist_af"

echo "==> docs/md/modules.md"
for cat in ai networking dev system gaming notes media graphics video audio browsers editors virt productivity infra; do
    table=$(build_doc_table "$cat")
    [[ -z "$table" ]] && continue
    splice "$DOCS" "$cat" "${table}"$'\n' md
done

echo "Done."
