#!/usr/bin/env bash
# Install thepeoples.io operator SSH public keys into an authorized_keys file.
#
# Default: dev-agent-01 (primary) and workstation (backup). Different keys.
# Optional: --with laptop
#
# Rewrites only the marked block this script owns. Every other line stays.
# Does not fetch keys and does not use sudo. The list below is the published list.

set -euo pipefail

BEGIN_MARK='# BEGIN thepeoples.io ssh-roster'
END_MARK='# END thepeoples.io ssh-roster'

fail() { printf '[ERROR] %s\n' "$*" >&2; exit 1; }
log() { printf '[INFO] %s\n' "$*" >&2; }

usage() {
    cat <<'EOF'
Usage: install-authorized-keys.sh [--with NAME]... [--file PATH] [--dry-run] [--list]

Installs published operator public keys into an OpenSSH authorized_keys file.
Default names are dev-agent-01 and workstation. They are different keys.
dev-agent-01 is the primary key. workstation is the backup key.
Repeat --with to add an optional name. The optional name is laptop.

  --with NAME   Add an optional name. Known name: laptop.
  --file PATH   authorized_keys path. Default: ~/.ssh/authorized_keys
  --dry-run     Print the file that would be written. Do not create or edit it.
  --list        Show every published name, then exit.
  --help        Show this help.

The script replaces only the block between these markers:

  # BEGIN thepeoples.io ssh-roster
  # END thepeoples.io ssh-roster

Lines outside that block are kept. A half-written block or a symlink target
is refused. ~/.ssh is created mode 0700 when it is missing, and the key file
is written mode 0600.
EOF
}

host_ids=()
host_modes=()
host_lines=()

load_roster() {
    local id mode keytype keymat
    while IFS='|' read -r id mode keytype keymat; do
        [[ -z "$id" || "${id:0:1}" == "#" ]] && continue
        [[ "$mode" == "default" || "$mode" == "optional" ]] || fail "bad roster mode for ${id}"
        [[ "$keytype" == "ssh-ed25519" ]] || fail "roster key for ${id} is not ssh-ed25519"
        [[ "$keymat" =~ ^[A-Za-z0-9+/]+=*$ ]] || fail "roster key for ${id} is not public key material"
        host_ids+=("$id")
        host_modes+=("$mode")
        host_lines+=("${keytype} ${keymat}")
    done <<'EOF'
# id|mode|type|public-key
dev-agent-01|default|ssh-ed25519|AAAAC3NzaC1lZDI1NTE5AAAAIN+4N3nHRj+bX8KrRVRYjQ8RBLv/W1PJTeud8klyKnma
workstation|default|ssh-ed25519|AAAAC3NzaC1lZDI1NTE5AAAAICzfcN1W28jPcYWMAkaJC7L6ouJdEuYobfqB9XYze/fc
laptop|optional|ssh-ed25519|AAAAC3NzaC1lZDI1NTE5AAAAIFPKq9T9Mfz19EB6K4R1qyLkHdyh1LXU0ckB5y3qZ6W4
EOF
}

index_of() {
    local want="$1" i
    for i in "${!host_ids[@]}"; do
        if [[ "${host_ids[$i]}" == "$want" ]]; then
            printf '%s' "$i"
            return 0
        fi
    done
    return 1
}

fingerprint_of() {
    local line="$1" tmp fp
    tmp="$(mktemp)"
    printf '%s\n' "$line" > "$tmp"
    if command -v ssh-keygen >/dev/null 2>&1; then
        fp="$(ssh-keygen -lf "$tmp" | awk '{print $2}')"
        printf '%s' "$fp"
    else
        printf '%s' "-"
    fi
    rm -f "$tmp"
}

show_list() {
    local i when fp
    printf '%-16s %-16s %s\n' "NAME" "WHEN" "FINGERPRINT"
    for i in "${!host_ids[@]}"; do
        if [[ "${host_modes[$i]}" == "default" ]]; then
            when="default"
        else
            when="--with ${host_ids[$i]}"
        fi
        fp="$(fingerprint_of "${host_lines[$i]}")"
        printf '%-16s %-16s %s\n' "${host_ids[$i]}" "$when" "$fp"
    done
}

strip_block() {
    local src="$1" dest="$2"
    awk -v begin="$BEGIN_MARK" -v end="$END_MARK" '
        $0 == begin {
            if (inside || seen) exit 2
            seen = 1
            inside = 1
            next
        }
        $0 == end {
            if (!inside) exit 3
            inside = 0
            next
        }
        inside { next }
        { print }
        END { if (inside) exit 3 }
    ' "$src" > "$dest"
}

load_roster

with_hosts=()
target=""
dry_run=false
list_only=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --with)
            [[ $# -ge 2 ]] || fail "--with needs a name"
            [[ "$2" =~ ^[A-Za-z0-9._-]+$ ]] || fail "unknown name: $2"
            with_hosts+=("$2")
            shift 2
            ;;
        --file)
            [[ $# -ge 2 ]] || fail "--file needs a path"
            target="$2"
            shift 2
            ;;
        --dry-run)
            dry_run=true
            shift
            ;;
        --list)
            list_only=true
            shift
            ;;
        --help|-h)
            usage
            exit 0
            ;;
        *)
            fail "unknown argument: $1"
            ;;
    esac
done

if [[ "$list_only" == true ]]; then
    show_list
    exit 0
fi

selected_ids=()
for i in "${!host_ids[@]}"; do
    if [[ "${host_modes[$i]}" == "default" ]]; then
        selected_ids+=("${host_ids[$i]}")
    fi
done

if [[ ${#with_hosts[@]} -gt 0 ]]; then
    for extra in "${with_hosts[@]}"; do
        if ! index_of "$extra" >/dev/null; then
            fail "unknown name: ${extra} (run --list)"
        fi
        already=false
        for existing in "${selected_ids[@]}"; do
            [[ "$existing" == "$extra" ]] && already=true
        done
        if [[ "$already" == false ]]; then
            selected_ids+=("$extra")
        fi
    done
fi

lines=()
seen=$'\n'
for i in "${!host_ids[@]}"; do
    wanted=false
    for sel in "${selected_ids[@]}"; do
        [[ "$sel" == "${host_ids[$i]}" ]] && wanted=true
    done
    [[ "$wanted" == true ]] || continue
    line="${host_lines[$i]}"
    case "$seen" in
        *$'\n'"$line"$'\n'*) continue ;;
    esac
    seen="${seen}${line}"$'\n'
    lines+=("$line")
done

[[ ${#lines[@]} -gt 0 ]] || fail "refusing to write an empty roster block"
hosts_label="$(printf '%s\n' "${selected_ids[@]}" | LC_ALL=C sort | paste -sd ' ' -)"

if [[ -z "$target" ]]; then
    [[ -n "${HOME:-}" ]] || fail "HOME is unset; pass --file"
    target="${HOME}/.ssh/authorized_keys"
fi
[[ "$target" != */ ]] || fail "refusing a directory path: ${target}"
[[ ! -d "$target" ]] || fail "refusing a directory path: ${target}"
[[ ! -L "$target" ]] || fail "refusing to follow a symlink: ${target}"

tmp="$(mktemp)"
cleanup() { rm -f "$tmp"; }
trap cleanup EXIT

if [[ -f "$target" ]]; then
    set +e
    strip_block "$target" "$tmp"
    status=$?
    set -e
    case "$status" in
        0) ;;
        2) fail "more than one thepeoples.io ssh-roster block in ${target}" ;;
        3) fail "unfinished thepeoples.io ssh-roster block in ${target}" ;;
        *) fail "could not read ${target}" ;;
    esac
else
    : > "$tmp"
fi

{
    printf '%s\n' "$BEGIN_MARK"
    printf '# names: %s\n' "$hosts_label"
    printf '%s\n' "${lines[@]}"
    printf '%s\n' "$END_MARK"
} >> "$tmp"

if [[ "$dry_run" == true ]]; then
    log "dry-run: would write ${target}"
    cat "$tmp"
    exit 0
fi

dest_dir="$(dirname -- "$target")"
if [[ ! -d "$dest_dir" ]]; then
    mkdir -p -- "$dest_dir"
    chmod 700 "$dest_dir"
elif [[ "$(basename -- "$dest_dir")" == ".ssh" ]]; then
    chmod 700 "$dest_dir"
fi

chmod 600 "$tmp"
mv -f -- "$tmp" "$target"
log "updated ${target} (names: ${hosts_label})"
