#!/usr/bin/env bash

# Personal content: the things that should be identical on every machine one
# person owns.
#
# Panama is meant to be installable by anybody, and it is also somebody's
# actual dotfiles. Those two goals only conflict if the personal half is mixed
# into the shared half, so it lives in one directory with one manifest, and
# this stage links it -- but only on a machine that said yes.
#
# A stranger who clones Panama gets user/ in their checkout and nothing linked
# from it. Their own content replaces it, or they delete it; either way their
# agent instructions are their own and their skills are their own. That is what
# makes tracking somebody's personal files in a public repository defensible.
#
# The answer comes from the interview as PANAMA_USER_CONTENT. Running this
# stage by hand outside an install honours the recorded answer instead, so
# `panama upgrade` on an already-configured machine does not need re-asking.

set -euo pipefail

log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }

PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
USER_DIR="$PANAMA_PATH/user"
MANIFEST="$USER_DIR/manifest"
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/panama"
DECISION="$STATE_DIR/user-content"
PANAMA_OLD="$PANAMA_PATH/config/old"

[[ -r "$MANIFEST" ]] || { log "No personal content manifest; nothing to link."; exit 0; }

# The interview's answer wins when there is one, and is remembered so a later
# run without it behaves the same way. A machine that has never been asked and
# is not being asked now links nothing, which is the safe direction: the cost
# of guessing yes is somebody else's agent instructions on your machine.
mkdir -p "$STATE_DIR"
if [[ -n "${PANAMA_USER_CONTENT:-}" ]]; then
    printf '%s\n' "$PANAMA_USER_CONTENT" >"$DECISION"
fi
decision="$( [[ -r "$DECISION" ]] && cat "$DECISION" || printf 'no' )"

if [[ "$decision" != "yes" ]]; then
    log "Personal content is not enabled on this machine; nothing linked."
    log "Enable it by re-running ./install and answering yes, or: echo yes > $DECISION"
    exit 0
fi

mkdir -p "$PANAMA_OLD"

# Moves whatever is already at a destination out of the way, once. A real file
# somebody has is never deleted: it goes to config/old/ under a name that says
# where it came from, which is the same promise link-dotfiles makes.
displace() {
    local destination="$1" backup
    if [[ -L "$destination" ]]; then
        rm -f "$destination"
        return 0
    fi
    [[ -e "$destination" ]] || return 0

    backup="$PANAMA_OLD/user-$(printf '%s' "${destination#"$HOME"/}" | tr '/' '-')"
    if [[ -e "$backup" ]]; then
        backup="$backup.$(date +%s)"
    fi
    mv "$destination" "$backup"
    log "Moved existing $destination to $backup"
}

linked=0
copied=0

while read -r kind source destination; do
    [[ -n "${kind:-}" ]] || continue
    [[ "$kind" == \#* ]] && continue

    src="$USER_DIR/$source"
    dst="${destination/#\~/$HOME}"

    if [[ ! -e "$src" ]]; then
        log "Skipping $source: it is not in user/"
        continue
    fi

    parent="$(dirname "$dst")"
    mkdir -p "$parent"
    # ssh refuses to read a config out of a directory anyone else can write,
    # and the default umask here would have created one.
    [[ "$parent" == "$HOME/.ssh" ]] && chmod 700 "$parent"

    case "$kind" in
        link)
            displace "$dst"
            ln -s "$src" "$dst"
            log "Linked $source → $dst"
            linked=$(( linked + 1 ))
            ;;
        linkdir)
            # A destination that has to hold more than this one source. The
            # only one today is ~/.claude/skills, which now carries Panama's
            # own shipped skills as well as these -- so the directory itself
            # cannot be a symlink, and each child is linked into it instead.
            #
            # Whatever link-skills put there is kept; only same-named entries
            # are displaced, which is how a personal skill deliberately
            # shadows a shipped one. This stage runs last for that reason.
            if [[ -L "$dst" ]]; then
                rm -f "$dst"
            elif [[ -e "$dst" && ! -d "$dst" ]]; then
                displace "$dst"
            fi
            mkdir -p "$dst"

            for child in "$src"/*; do
                [[ -e "$child" ]] || continue
                child_name="$(basename "$child")"
                displace "$dst/$child_name"
                ln -s "$child" "$dst/$child_name"
                log "Linked $source/$child_name → $dst/$child_name"
                linked=$(( linked + 1 ))
            done
            ;;
        copy)
            if [[ -e "$dst" ]]; then
                log "Keeping existing $dst"
            else
                cp -r "$src" "$dst"
                log "Copied $source → $dst"
                copied=$(( copied + 1 ))
            fi
            ;;
        *)
            log "Skipping unknown manifest kind: $kind"
            ;;
    esac
done < <(grep -vE '^\s*(#|$)' "$MANIFEST")

log "Personal content: $linked linked, $copied copied."
