#!/usr/bin/env bash
set -euo pipefail
# POS: system backup — Encrypted (AES-256) folder snapshots (tar + gpg)
# POS_FLAGS: --service --no-encrypt
# POS_SUBCMDS: menu
# POS_CONFIG: notify | notify.env | NOTIFY_PLATFORM=:Comma-separated notify platforms (default telegram) — shared by backup, firewall, share nfs client/server
# POS_DEPS: tar

source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
source "$(dirname "$0")/../lib/notify.sh" 2>/dev/null || source "$(dirname "$0")/notify.sh"
source "$(dirname "$0")/../lib/usb-lib.sh" 2>/dev/null || source "$(dirname "$0")/usb-lib.sh"
source "$(dirname "$0")/../lib/menu-lib.sh" 2>/dev/null || source "$(dirname "$0")/menu-lib.sh"

load_system_env
EFF_ROOTS="${BACKUP_SERVICE_ROOTS:-/srv $HOME/srv}"

trap 'notify_send "Backup FAILED: ${FOLDER:-unknown}"' ERR

usage() {
    cat <<EOF
Usage: pos system backup <folder-path>
       pos system backup <folder-path> --no-encrypt
       pos system backup --service

Create a gpg-encrypted (AES-256) tar.gz snapshot of a folder and verify it.
The archive password is prompted twice and never stored. With --no-encrypt
(or BACKUP_ENCRYPT=0) the backup is kept as a plain .tar.gz — no password,
headless/cron safe.

Modes:
  <folder-path>   Back up that folder directly.
  --no-encrypt    Skip encryption (no password prompt, artifact stays .tar.gz).
  --service       List folders under /srv and ~/srv, pick one, back it up.

Bare \`pos system backup\` on a terminal (or \`pos system backup menu\`)
opens an interactive menu wrapping these modes; arguments stay scriptable.

The final artifact <name>_<date>.tar.gz[.gpg] is written to the current directory.
After it verifies, connected USB storage is offered: the copy lands in
<usb>/backups/ and is sha256-verified 100% before it is announced. A stick
that is plugged in but not mounted is offered a mount first (sudo, mirrors
the usb-automount scheme) before the copy.

Environment:
  BACKUP_ENCRYPT       Set to 0 to skip encryption (same as --no-encrypt)
                       (default: 1)
  BACKUP_SERVICE_ROOTS   Space-separated roots for --service
                         (effective: ${EFF_ROOTS})
  BACKUP_USB_ROOT        USB root to copy finished backups to
                         (default: auto-detect mounted USB storage)
  BACKUP_MOUNT_BASE      Where to mount an unmounted USB stick offered
                         during the copy (default: /media; alias of
                         USB_MOUNT_BASE)
  BACKUP_USB_BYID        by-id dir used to corroborate USB detection
                         (default: /dev/disk/by-id; alias of USB_BYID)
  (loaded from ~/.config/linux_post_install/system.env unless exported)
EOF
    exit 0
}

command -v tar &>/dev/null || err "tar not found"

# ── USB copy (optional post-backup step) ─────────────────────────
# Detection runs AFTER the backup finished, so a stick plugged in while
# the archive was being made is found. The copy lands in <usb>/backups/
# and the transfer is proven 100% (sha256 source vs copy) before any
# success is announced. BACKUP_USB_ROOT pins the root and skips
# detection; otherwise usb_pick_root (lib/usb-lib.sh) auto-detects,
# offers to mount unmounted sticks, and picks the target.
usb_copy_offer() {
    local archive="$1" root="" dest_dir="" dest="" src_sum="" dst_sum=""

    command -v lsblk &>/dev/null || { warn "lsblk not found — USB copy skipped"; return 0; }
    command -v jq    &>/dev/null || { warn "jq not found — USB copy skipped"; return 0; }

    section "USB copy"

    if [ -n "${BACKUP_USB_ROOT:-}" ]; then
        root="$BACKUP_USB_ROOT"
        if ! confirm "Copy backup to ${root%/}/backups/?" n; then
            log "Skipped — backup stays local: $archive"
            return 0
        fi
    else
        usb_pick_root "Copy backup to" "backups" "backup stays local: $archive" || {
            log "Skipped — backup stays local: $archive"
            return 0
        }
        root="$USB_ROOT"
    fi

    dest_dir="${root%/}/backups"
    dest="$dest_dir/$(basename "$archive")"

    mkdir -p "$dest_dir"
    log "Copying to $dest ..."
    cp "$archive" "$dest_dir/"
    chmod 600 "$dest" 2>/dev/null \
        || warn "Could not chmod 600 the USB copy (vfat filesystem?)"

    log "Verifying transfer (sha256)..."
    src_sum="$(sha256sum "$archive" | cut -d' ' -f1)"
    dst_sum="$(sha256sum "$dest" | cut -d' ' -f1)"
    if [ "$src_sum" != "$dst_sum" ]; then
        warn "USB copy FAILED verification — checksum mismatch:"
        warn "  source: $src_sum  $archive"
        warn "  copy  : $dst_sum  $dest"
        notify_send "USB copy FAILED for $archive — checksum mismatch on $dest"
        exit 1
    fi
    ok "Transfer verified 100% (sha256 match): $dest"
    notify_send "Backup copied to USB: $dest (sha256 verified)"
}

# ── USB detection / mount offer / pick flow ─────────────────────
# Shared with pos-media-sync: lib/usb-lib.sh (usb_detect /
# usb_related_present / usb_mount_offer / usb_pick_root).
SERVICE=0
ENCRYPT=1
[ "${BACKUP_ENCRYPT:-1}" = "0" ] && ENCRYPT=0

# ── Folder picker over the --service roots ──────────────────────
# Sets $FOLDER; errors out when nothing can be offered (same as --service).
pick_service_folder() {
    if [ -n "${BACKUP_SERVICE_ROOTS:-}" ]; then
        read -r -a roots <<< "$BACKUP_SERVICE_ROOTS"
    else
        roots=(/srv "$HOME/srv")
    fi

    idx=0
    for root in "${roots[@]}"; do
        [ -d "$root" ] || { warn "Root not found: $root"; continue; }

        dirs=()
        while IFS= read -r d; do
            dirs+=("$d")
        done < <(find "$root" -maxdepth 1 -mindepth 1 -type d | sort)

        [ ${#dirs[@]} -gt 0 ] || { warn "No folders in $root"; continue; }

        echo
        echo "${root}:"
        for d in "${dirs[@]}"; do
            idx=$((idx + 1))
            names[$idx]="$d"
            printf "%2d) %s\n" "$idx" "$d"
        done
    done

    [ "$idx" -gt 0 ] || err "No folders found under: ${roots[*]}"

    read -rp "Select folder number: " choice
    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > idx )); then
        err "Invalid selection: $choice"
    fi
    FOLDER="${names[$choice]}"
}

# ── Backup flow ($FOLDER → timestamped archive, then USB copy offer) ──
run_backup() {
    [ -d "$FOLDER" ] || err "Folder not found: $FOLDER"

    NAME="$(basename "$FOLDER")"
    DATE="$(date +%Y-%m-%d_%H-%M-%S)"
    ARCHIVE="${NAME}_${DATE}.tar.gz"

    echo
    log "Creating backup..."
    echo "Source : $FOLDER"
    echo "Output : $ARCHIVE"

    sudo tar -czvf "$ARCHIVE" -C "$(dirname "$FOLDER")" "$NAME"

    log "Verifying archive..."
    tar -tzf "$ARCHIVE" > /dev/null
    log "Archive verified"

    if [ "$ENCRYPT" -eq 1 ]; then
        command -v gpg &>/dev/null || err "gpg not found (install gnupg)"

        while true; do
            read -s -rp "Enter backup password: " PASS
            echo
            read -s -rp "Confirm backup password: " CONFIRM
            echo
            if [ -n "$PASS" ] && [ "$PASS" = "$CONFIRM" ]; then
                break
            fi
            warn "Passwords are empty or do not match — try again"
        done
        unset CONFIRM

        log "Encrypting backup..."
        gpg --batch --yes --passphrase "$PASS" --symmetric --cipher-algo AES256 "$ARCHIVE"

        rm -f "$ARCHIVE"
        ARCHIVE="${ARCHIVE}.gpg"
        chmod 600 "$ARCHIVE"

        log "Verifying encrypted backup..."
        gpg --batch --quiet --passphrase "$PASS" --decrypt "$ARCHIVE" | tar -tzf - > /dev/null

        unset PASS
    else
        chmod 600 "$ARCHIVE"
        log "No encryption requested — keeping $ARCHIVE"
    fi
    echo
    log "Backup completed: $ARCHIVE"
    notify_send "Backup completed: $ARCHIVE"

    # Optional: detect a USB stick connected after the backup finished, offer to
    # copy the archive to <usb>/backups/, and prove the transfer 100%. From here
    # on a failure is a USB-copy problem, not a backup problem.
    trap 'notify_send "USB copy FAILED: ${ARCHIVE:-unknown}"' ERR
    usb_copy_offer "$ARCHIVE"
}

# ── Interactive menu (opt-in front door, Pattern B via lib/menu-lib.sh) ──
menu_backup_folder() {
    local enc="$1" val prompt
    val="$(menu_ask_value "Folder to back up")" || return 0
    if [ ! -d "$val" ]; then
        warn "Not a folder: $val"
        return 0
    fi
    FOLDER="$val"
    if [ "$enc" -eq 1 ]; then
        prompt="Create ENCRYPTED backup of $FOLDER?"
    else
        prompt="Create UNENCRYPTED backup of $FOLDER (plain .tar.gz, no password)?"
    fi
    confirm "$prompt" n || { log "Cancelled"; return 0; }
    ENCRYPT="$enc"
    run_backup
}

menu_backup_service() {
    if ! pick_service_folder; then
        warn "Cancelled — no folder selected"
        return 0
    fi
    confirm "Create ENCRYPTED backup of $FOLDER?" n || { log "Cancelled"; return 0; }
    ENCRYPT=1
    run_backup
}

run_menu() {
    menu_guard || exit 1
    while true; do
        local choice
        choice="$(menu_run "System backup" \
            "New encrypted backup (type/paste folder)" \
            "New encrypted backup — pick from ${EFF_ROOTS}" \
            "New backup WITHOUT encryption (type/paste folder)")" || return 0
        case "$choice" in
            1) menu_backup_folder 1 ;;
            2) menu_backup_service ;;
            3) menu_backup_folder 0 ;;
        esac
    done
}

# Menu door: explicit verb, or zero args on a terminal. Everything below —
# including zero args without a terminal — stays byte-compatible with the
# pre-menu CLI.
if [ "${1:-}" = "menu" ]; then
    run_menu
    exit 0
fi

for arg in "$@"; do
    case "$arg" in
        -h|--help) usage ;;
        --service) SERVICE=1 ;;
        --no-encrypt) ENCRYPT=0 ;;
        *) FOLDER="$arg" ;;
    esac
done

if [ $# -eq 0 ] && [ -t 0 ]; then
    run_menu
    exit 0
fi

{ [ "$SERVICE" -eq 1 ] || [ -n "${FOLDER:-}" ]; } || err "Missing folder path (or use --service)"

if [ "$SERVICE" -eq 1 ]; then
    pick_service_folder
fi

run_backup
