#!/usr/bin/env bash
# update-configs — sync dotfiles configs into ~/.config
#
# Config:   ~/.config/config-updater/updater.conf
# Manifest: ~/.config/config-updater/manifest

set -euo pipefail

CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/config-updater"
CONF_FILE="${CONF_DIR}/updater.conf"
MANIFEST="${CONF_DIR}/manifest"
TARGET="${XDG_CONFIG_HOME:-$HOME/.config}"

RED='\033[0;31m'; YEL='\033[1;33m'; GRN='\033[0;32m'; DIM='\033[2m'; RST='\033[0m'
err()  { printf "${RED}✖ %s${RST}\n" "$*" >&2; }
warn() { printf "${YEL}⚠ %s${RST}\n" "$*"; }
ok()   { printf "${GRN}✔ %s${RST}\n" "$*"; }
note() { printf "${DIM}  %s${RST}\n" "$*"; }
die()  { err "$*"; exit 1; }

[[ -f "$CONF_FILE" ]] || die "Config not found: $CONF_FILE"

# ── parse updater.conf ────────────────────────────────────────────────────────
SOURCE_BASE=""
declare -A ENTRY_TYPE   # name → config | flat | ignore

while IFS= read -r line; do
    line="${line%%#*}"
    line="${line#"${line%%[! ]*}"}"
    line="${line%"${line##*[! ]}"}"
    [[ -z "$line" ]] && continue

    if [[ "$line" =~ ^SOURCE_BASE[[:space:]]*=[[:space:]]*(.+)$ ]]; then
        src="${BASH_REMATCH[1]%"${BASH_REMATCH[1]##*[! ]}"}"
        SOURCE_BASE="${src/#\~/$HOME}"
        continue
    fi

    read -r type name _rest <<< "$line"
    [[ -z "${name:-}" ]] && continue
    case "$type" in
        config|flat|ignore) ENTRY_TYPE["$name"]="$type" ;;
        *) warn "Unknown type '$type' for '$name' — skipping" ;;
    esac
done < "$CONF_FILE"

[[ -n "$SOURCE_BASE" ]] || die "SOURCE_BASE not defined in $CONF_FILE"
[[ -d "$SOURCE_BASE" ]] || die "SOURCE_BASE not found: $SOURCE_BASE"

# ── load previous manifest ────────────────────────────────────────────────────
declare -A PREV_MANIFEST
if [[ -f "$MANIFEST" ]]; then
    while IFS=: read -r mtype mname; do
        [[ -n "${mtype:-}" && -n "${mname:-}" ]] && PREV_MANIFEST["$mname"]="$mtype"
    done < "$MANIFEST"
fi

warned=0

# ── warn: manifest entries no longer in updater.conf ─────────────────────────
for mname in "${!PREV_MANIFEST[@]}"; do
    [[ -n "${ENTRY_TYPE[$mname]:-}" ]] && continue
    warn "'$mname' was previously managed but is no longer in updater.conf"
    note "Remove ~/.config/$mname manually if it is no longer needed"
    warned=1
done

# ── warn: source items not covered by updater.conf ───────────────────────────
while IFS= read -r -d '' item; do
    name="$(basename "$item")"
    [[ "$name" == .* ]] && continue
    [[ -n "${ENTRY_TYPE[$name]:-}" ]] && continue
    warn "Untracked source item: '$name' — add to updater.conf (config / flat / ignore)"
    warned=1
done < <(find "$SOURCE_BASE" -maxdepth 1 -mindepth 1 -print0 | sort -z)

(( warned )) && printf '\n'

# ── apply ─────────────────────────────────────────────────────────────────────
errors=0
new_manifest=()

for name in "${!ENTRY_TYPE[@]}"; do
    type="${ENTRY_TYPE[$name]}"
    [[ "$type" == ignore ]] && continue

    src="${SOURCE_BASE}/${name}"
    if [[ ! -e "$src" ]]; then
        err "Source missing: $src"
        (( errors++ )) || true
        continue
    fi

    case "$type" in
        config)
            rm -rf "${TARGET:?}/${name}"
            cp -r "$src" "$TARGET/$name"
            ok "config  $name"
            ;;
        flat)
            [[ -d "$src" ]] || {
                err "flat entry '$name' must be a directory: $src"
                (( errors++ )) || true
                continue
            }
            cp -r "${src}/." "$TARGET/"
            ok "flat    $name → (contents into ~/.config/)"
            ;;
    esac

    new_manifest+=("${type}:${name}")
done

# ── write manifest ────────────────────────────────────────────────────────────
printf '%s\n' "${new_manifest[@]}" | sort > "$MANIFEST"

printf '\n'
if (( errors > 0 )); then
    err "$errors error(s) — manifest may be incomplete"
    exit 1
else
    ok "Done — manifest updated at $MANIFEST"
fi
