#!/usr/bin/env bash

# ============================================================
# SMR-SRV-OT-SHARE
# Backup critical server configuration
#
# Output:
#   CONFIG archive  - ordinary configuration and inventory
#   SECRETS archive - Samba/File Browser databases, encrypted
#   SHA256SUMS      - archive integrity
#
# Destination:
#   /srv/main-data/baltikashare/ot-admin/server-backups
# ============================================================

set -Eeuo pipefail
umask 077

HOST="$(hostname -s)"
STAMP="$(date '+%Y%m%d-%H%M%S')"

DEST="/srv/main-data/baltikashare/ot-admin/server-backups"
WORK="$(mktemp -d "/var/tmp/${HOST}-config-backup.XXXXXX")"

CONFIG_ROOT="${WORK}/config"
SECRET_ROOT="${WORK}/secrets"
INVENTORY="${CONFIG_ROOT}/inventory"

CONFIG_ARCHIVE="${DEST}/${HOST}-config-${STAMP}.tar.gz"
SECRET_PLAIN="${WORK}/${HOST}-secrets-${STAMP}.tar.gz"
SECRET_ARCHIVE="${DEST}/${HOST}-secrets-${STAMP}.tar.gz.enc"
SUMS="${DEST}/SHA256SUMS-${STAMP}.txt"

STOPPED_SERVICES=()

cleanup()
{
    # Если скрипт оборвался во время резервирования БД,
    # вернуть остановленные сервисы.
    for svc in "${STOPPED_SERVICES[@]:-}"
    do
        systemctl start "$svc" >/dev/null 2>&1 || true
    done

    rm -rf "$WORK"
}

trap cleanup EXIT

echo "============================================================"
echo "SMR-SRV-OT-SHARE configuration backup"
echo "Host:        $HOST"
echo "Timestamp:   $STAMP"
echo "Destination: $DEST"
echo "============================================================"
echo

# ------------------------------------------------------------
# Safety checks
# ------------------------------------------------------------

if ! mountpoint -q /srv/main-data
then
    echo "ERROR: /srv/main-data is not mounted."
    exit 1
fi

FS_TYPE="$(findmnt -n -o FSTYPE /srv/main-data 2>/dev/null || true)"

if [[ "$FS_TYPE" != "btrfs" ]]
then
    echo "ERROR: /srv/main-data is '$FS_TYPE', expected btrfs."
    exit 1
fi

install -d \
    -o root \
    -g admins \
    -m 0770 \
    "$DEST"

mkdir -p "$CONFIG_ROOT" "$SECRET_ROOT" "$INVENTORY"

# ------------------------------------------------------------
# Helper: copy existing path while preserving original tree
# ------------------------------------------------------------

copy_path()
{
    local SRC="$1"

    if [[ -e "$SRC" || -L "$SRC" ]]
    then
        echo "COPY: $SRC"
        cp -a --parents "$SRC" "$CONFIG_ROOT"
    else
        echo "SKIP: $SRC does not exist"
    fi
}

# ============================================================
# 1. STATIC CONFIGURATION
# ============================================================

echo
echo "=== Static configuration ==="

# Samba.
copy_path /etc/samba

# Network.
copy_path /etc/netplan
copy_path /etc/hostname
copy_path /etc/hosts
copy_path /etc/nsswitch.conf
copy_path /etc/fstab

# systemd-resolved / NTP.
copy_path /etc/systemd/resolved.conf
copy_path /etc/systemd/timesyncd.conf

# Kernel/network tuning.
copy_path /etc/sysctl.conf
copy_path /etc/sysctl.d

# Firewall.
copy_path /etc/ufw

# Snapper.
copy_path /etc/snapper
copy_path /etc/default/snapper

# Samba protection against missing MAIN-DATA.
copy_path /etc/systemd/system/smbd.service.d

# Recycle cleanup.
copy_path /usr/local/sbin/samba-recycle-cleanup
copy_path /etc/systemd/system/samba-recycle-cleanup.service
copy_path /etc/systemd/system/samba-recycle-cleanup.timer

# DDNS.
copy_path /usr/local/sbin/ot-ddns-register
copy_path /etc/systemd/system/ot-ddns-register.service

# File Browser.
copy_path /etc/filebrowser
copy_path /etc/systemd/system/filebrowser.service
copy_path /usr/local/bin/filebrowser

# Этот backup-скрипт.
copy_path /usr/local/sbin/share-config-backup

# Linux user/group database.
# /etc/shadow намеренно НЕ входит в обычный архив.
copy_path /etc/passwd
copy_path /etc/group

# SSH configuration, но без private host keys.
copy_path /etc/ssh/sshd_config
copy_path /etc/ssh/sshd_config.d

# APT sources.
copy_path /etc/apt/sources.list
copy_path /etc/apt/sources.list.d

# ============================================================
# 2. CURRENT STATE / INVENTORY
# ============================================================

echo
echo "=== Collecting inventory ==="

hostnamectl > "${INVENTORY}/hostnamectl.txt" 2>&1 || true

ip -br link \
    > "${INVENTORY}/ip-link.txt" 2>&1 || true

ip -br address \
    > "${INVENTORY}/ip-address.txt" 2>&1 || true

ip route show table all \
    > "${INVENTORY}/ip-route-all.txt" 2>&1 || true

ip rule show \
    > "${INVENTORY}/ip-rule.txt" 2>&1 || true

resolvectl status \
    > "${INVENTORY}/resolvectl-status.txt" 2>&1 || true

timedatectl \
    > "${INVENTORY}/timedatectl.txt" 2>&1 || true

lsblk -o \
NAME,PATH,SIZE,TYPE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS \
    > "${INVENTORY}/lsblk.txt" 2>&1 || true

blkid \
    > "${INVENTORY}/blkid.txt" 2>&1 || true

findmnt \
    > "${INVENTORY}/findmnt.txt" 2>&1 || true

findmnt /srv/main-data \
    > "${INVENTORY}/findmnt-main-data.txt" 2>&1 || true

btrfs filesystem show /srv/main-data \
    > "${INVENTORY}/btrfs-filesystem-show.txt" 2>&1 || true

btrfs filesystem usage /srv/main-data \
    > "${INVENTORY}/btrfs-filesystem-usage.txt" 2>&1 || true

btrfs subvolume list -t /srv/main-data \
    > "${INVENTORY}/btrfs-subvolumes.txt" 2>&1 || true

snapper list-configs \
    > "${INVENTORY}/snapper-list-configs.txt" 2>&1 || true

# Samba effective configuration.
testparm -s \
    > "${INVENTORY}/samba-testparm.txt" 2>&1 || true

# Samba accounts WITHOUT passwords.
pdbedit -L -v \
    > "${INVENTORY}/samba-users.txt" 2>&1 || true

# Linux IDs and groups.
getent passwd \
    > "${INVENTORY}/passwd-getent.txt"

getent group \
    > "${INVENTORY}/group-getent.txt"

# Packages.
dpkg-query -W \
    -f='${binary:Package}\t${Version}\n' \
    > "${INVENTORY}/packages.txt" 2>&1 || true

apt-mark showmanual \
    > "${INVENTORY}/packages-manual.txt" 2>&1 || true

# Services.
systemctl is-enabled \
    smbd \
    filebrowser \
    samba-recycle-cleanup.timer \
    ot-ddns-register.service \
    > "${INVENTORY}/services-enabled.txt" 2>&1 || true

systemctl is-active \
    smbd \
    filebrowser \
    samba-recycle-cleanup.timer \
    > "${INVENTORY}/services-active.txt" 2>&1 || true

systemctl list-timers --all \
    > "${INVENTORY}/systemd-timers.txt" 2>&1 || true

ss -lntup \
    > "${INVENTORY}/listening-ports.txt" 2>&1 || true

ufw status verbose \
    > "${INVENTORY}/ufw-status.txt" 2>&1 || true

# ============================================================
# 3. IMPORTANT ACL STATE
# ============================================================

echo
echo "=== Collecting ACL state ==="

ACL="${INVENTORY}/acl-important.txt"
BASE="/srv/main-data/baltikashare"

: > "$ACL"

for share in \
    clonezilla \
    exchange \
    pivoproduction \
    rozliv \
    utilities \
    software \
    documentation \
    ot-admin
do
    DIR="${BASE}/${share}"

    if [[ -d "$DIR" ]]
    then
        echo >> "$ACL"
        echo "==================================================" >> "$ACL"
        echo "SHARE: $share" >> "$ACL"
        echo "==================================================" >> "$ACL"

        getfacl -p "$DIR" >> "$ACL" 2>&1 || true

        if [[ -d "${DIR}/.recycle" ]]
        then
            echo >> "$ACL"
            echo "--- .recycle ---" >> "$ACL"

            getfacl -p "${DIR}/.recycle" \
                >> "$ACL" 2>&1 || true

            find "${DIR}/.recycle" \
                -mindepth 1 \
                -maxdepth 1 \
                -type d \
                -exec getfacl -p {} + \
                >> "$ACL" 2>&1 || true
        fi
    fi
done

# ============================================================
# 4. RESTORE NOTES
# ============================================================

cat > "${CONFIG_ROOT}/RESTORE_ORDER.txt" <<'EOF'
SMR-SRV-OT-SHARE
Recommended restore order
=========================

1. Install Ubuntu Server and required packages.

2. Recreate local users/groups while preserving required UID/GID
   where existing MAIN-DATA files are retained.

3. Restore:
   /etc/netplan
   /etc/fstab
   /etc/hostname
   /etc/hosts
   relevant sysctl/systemd configuration.

4. Mount MAIN-DATA and verify:
   mountpoint /srv/main-data
   findmnt /srv/main-data

5. Restore Snapper configuration.

6. Restore scripts:
   /usr/local/sbin/samba-recycle-cleanup
   /usr/local/sbin/ot-ddns-register

7. Restore systemd units:
   samba-recycle-cleanup.service/timer
   smbd.service.d/main-data.conf
   filebrowser.service
   ot-ddns-register.service

8. Restore /etc/samba/smb.conf.

9. Restore Samba private database from encrypted SECRETS archive
   ONLY with Samba stopped.

10. Restore File Browser database from encrypted SECRETS archive
    ONLY with File Browser stopped.

11. Run:
    systemctl daemon-reload
    testparm -s
    netplan generate

12. Verify POSIX ACL against:
    inventory/acl-important.txt

13. Start and verify services.

14. Check:
    Samba RW/RO matrix
    Snapper Previous Versions
    recycle
    File Browser
    DDNS A/PTR
    split DNS
EOF

# ============================================================
# 5. CREATE NORMAL CONFIG ARCHIVE
# ============================================================

echo
echo "=== Creating configuration archive ==="

tar \
    --acls \
    --xattrs \
    --numeric-owner \
    -C "$CONFIG_ROOT" \
    -czf "$CONFIG_ARCHIVE" \
    .

chown root:admins "$CONFIG_ARCHIVE"
chmod 0640 "$CONFIG_ARCHIVE"

# ============================================================
# 6. CREATE CONSISTENT SECRET DATABASE COPY
# ============================================================

echo
echo "============================================================"
echo "Sensitive database backup"
echo
echo "Samba/File Browser will be stopped briefly."
echo "Do not run this script during active file transfers."
echo "============================================================"
echo

for svc in smbd nmbd winbind filebrowser
do
    if systemctl is-active --quiet "$svc"
    then
        echo "STOP: $svc"
        systemctl stop "$svc"
        STOPPED_SERVICES+=("$svc")
    fi
done

# Samba local credential/state databases.
if [[ -d /var/lib/samba ]]
then
    cp -a --parents /var/lib/samba "$SECRET_ROOT"
fi

# File Browser database contains web account information.
if [[ -f /var/lib/filebrowser/filebrowser.db ]]
then
    cp -a --parents \
        /var/lib/filebrowser/filebrowser.db \
        "$SECRET_ROOT"
fi

sync

# Bring services back before encryption to minimize downtime.
for svc in "${STOPPED_SERVICES[@]:-}"
do
    echo "START: $svc"
    systemctl start "$svc"
done

STOPPED_SERVICES=()

# Create unencrypted temporary archive.
tar \
    --acls \
    --xattrs \
    --numeric-owner \
    -C "$SECRET_ROOT" \
    -czf "$SECRET_PLAIN" \
    .

echo
echo "============================================================"
echo "Enter a password for the encrypted SECRETS archive."
echo "IMPORTANT: without this password the archive cannot be restored."
echo "============================================================"
echo

openssl enc \
    -aes-256-cbc \
    -salt \
    -pbkdf2 \
    -iter 200000 \
    -in "$SECRET_PLAIN" \
    -out "$SECRET_ARCHIVE"

rm -f "$SECRET_PLAIN"

chown root:admins "$SECRET_ARCHIVE"
chmod 0640 "$SECRET_ARCHIVE"

# ============================================================
# 7. SHA256
# ============================================================

(
    cd "$DEST"

    sha256sum \
        "$(basename "$CONFIG_ARCHIVE")" \
        "$(basename "$SECRET_ARCHIVE")"
) > "$SUMS"

chown root:admins "$SUMS"
chmod 0640 "$SUMS"

echo
echo "============================================================"
echo "BACKUP COMPLETED"
echo "============================================================"
echo
echo "CONFIG:"
echo "  $CONFIG_ARCHIVE"
echo
echo "SECRETS:"
echo "  $SECRET_ARCHIVE"
echo
echo "CHECKSUMS:"
echo "  $SUMS"
echo
echo "Copy all three files to an external PC/storage."
echo "============================================================"
