#!/usr/bin/env bash

# Snapshots of the Panama settings store.
#
# The whole desktop configuration is one JSON file, which makes a backup a copy
# and a restore an overwrite. That is worth exposing: the settings app now
# changes real things -- compositor geometry, idle timeouts, the dock -- and
# being able to get back to a known-good state without hunting through git is
# the difference between experimenting freely and being cautious.
#
#   panama-settings-backup save            snapshot the current settings
#   panama-settings-backup list            JSON list of snapshots, newest first
#   panama-settings-backup restore <name>  replace settings with a snapshot
#
# Snapshots are validated as JSON on the way in and on the way out, so a
# truncated file can never be restored over a working configuration.
#
# Names carry milliseconds. At one-second resolution a save followed promptly by
# a restore produced the same filename twice, and the restore's own safety
# snapshot overwrote the very file it was about to read.

set -euo pipefail

settings="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
backup_dir="${XDG_STATE_HOME:-$HOME/.local/state}/panama/backups"
keep=15

fail() {
    printf '%s\n' "$1" >&2
    exit 1
}

case "${1:-list}" in
    save)
        [[ -r "$settings" ]] || fail "No settings file to back up."
        jq -e . "$settings" >/dev/null 2>&1 || fail "The current settings file is not valid JSON."
        mkdir -p "$backup_dir"
        stamp="$(date +%Y%m%d-%H%M%S%3N)"
        cp "$settings" "$backup_dir/settings-$stamp.json"
        # Keep the most recent few. A snapshot per change would otherwise grow
        # without bound in a directory nobody ever looks at.
        ls -1t "$backup_dir"/settings-*.json 2>/dev/null | tail -n +$((keep + 1)) | while read -r old; do
            rm -f "$old"
        done
        printf '{"saved":"settings-%s.json"}\n' "$stamp"
        ;;

    list)
        mkdir -p "$backup_dir"
        first=true
        printf '['
        for file in $(ls -1t "$backup_dir"/settings-*.json 2>/dev/null); do
            name="$(basename "$file")"
            # settings-20260818-004512.json -> 2026-08-18 00:45
            raw="${name#settings-}"; raw="${raw%.json}"
            pretty="${raw:0:4}-${raw:4:2}-${raw:6:2} ${raw:9:2}:${raw:11:2}:${raw:13:2}"
            keys="$(jq -r 'keys | length' "$file" 2>/dev/null || printf 0)"
            [[ "$first" == true ]] || printf ','
            first=false
            printf '{"name":"%s","when":"%s","keys":%s}' "$name" "$pretty" "$keys"
        done
        printf ']\n'
        ;;

    restore)
        name="${2:-}"
        [[ -n "$name" ]] || fail "Which snapshot?"
        # Only a bare filename from the backup directory, so a caller cannot
        # walk out of it with a path.
        [[ "$name" =~ ^settings-[0-9]{8}-[0-9]{9}\.json$ ]] || fail "Not a snapshot name."
        source_file="$backup_dir/$name"
        [[ -r "$source_file" ]] || fail "That snapshot is missing."
        jq -e . "$source_file" >/dev/null 2>&1 || fail "That snapshot is not valid JSON."

        # Snapshot what is being replaced, so restore is itself undoable.
        if [[ -r "$settings" ]] && jq -e . "$settings" >/dev/null 2>&1; then
            mkdir -p "$backup_dir"
            cp "$settings" "$backup_dir/settings-$(date +%Y%m%d-%H%M%S%3N).json"
        fi

        mkdir -p "$(dirname "$settings")"
        cp "$source_file" "$settings.tmp"
        mv "$settings.tmp" "$settings"
        printf '{"restored":"%s"}\n' "$name"
        ;;

    *)
        fail "usage: panama-settings-backup [save|list|restore <name>]"
        ;;
esac
