#!/bin/bash
# ==============================================================================================
# ============================= Container Data Export ==========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Exports a container's appdata directory to a compressed tar archive. Stops
# the container before archiving and restarts it after — ensures a clean,
# consistent backup. Use before major updates, pool migrations, destructive
# appdata operations, or when archiving a container being removed from the stack.
#
# Output: ContainerName_YYYY-MM-DD_HH-MM.tar.gz — timestamped, no overwrite.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Validate arguments — container name, appdata path, output directory all required
#   2. Verify the output directory exists and holds enough free space for the archive
#   3. Record whether the container is currently running
#   4. Stop the container if it was running
#   5. tar czf the appdata directory to a timestamped archive
#   6. Verify the archive with tar --test-file
#   7. Restart the container only if it was running before — a container found stopped
#      stays stopped
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# This tool takes everything as arguments rather than from conf:
#
#   <container>    Container to stop for the duration of the export
#   <appdata_path> Directory to archive
#   <output_dir>   Destination for the archive — must already exist
#
# That is deliberate. It is used for one-off exports of arbitrary containers, including
# ones being removed from the stack entirely, so there is no meaningful configured list
# to draw from and nothing host-specific to alias.
#
# Note the archive is written as root and is not chowned afterwards. That is fine for the
# operator-invoked use this tool is for, but worth knowing if the output directory is a
# user share reached over SMB.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Archive Verification Before Restart
#   The archive is tested with tar --test-file before the container is restarted.
#   A corrupt archive is not a usable backup — this catches tar failures, I/O
#   errors, and truncated writes before declaring success. If verification fails,
#   the container is still restarted (appdata is unchanged) and an error logged.
#
# Conservative Space Estimate
#   Required space is estimated as appdata size × 1.1 (10% buffer). The actual
#   compressed archive will typically be much smaller — database files compress
#   well, media files do not. The estimate is a conservative floor, not a
#   prediction.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Container Restart Rule
#   Tracks whether the container was running before the export. Running containers
#   are restarted after completion; already-stopped containers are left stopped.
#   The restart happens on every exit path — a failed tar does not leave the
#   container stuck stopped.
#
# Partial Archive Cleanup
#   If tar fails, the incomplete archive is removed. A partial archive is worse
#   than no archive — it can look valid but restore to an incomplete state.
#
# Docker Timeout
#   DOCKER_TIMEOUT (default: 30s) caps all docker calls. Guards against a hung
#   daemon blocking the script indefinitely.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# container_data_export.sh ContainerName /path/to/appdata /path/to/output/dir
#     Stop container, create archive, verify, restart container.
#     Example: container_data_export.sh Emby /mnt/media-servers/.../Emby /mnt/user/Backups/
#
# container_data_export.sh ContainerName /path/to/appdata /path/to/output/dir --dry-run
#     Show what would be archived and estimated size. No container stop, no tar.
#
# container_data_export.sh ContainerName /path/to/appdata /path/to/output/dir --log
#     Verbose output: space check, tar progress, verification result.
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "$SCRIPT_DIR/../load_config.sh"

parse_args "$@"

DOCKER_TIMEOUT=30   # longer timeout — stop can take time on large containers

# ── Positional args ───────────────────────────────────────────────────────────────────────────
CONTAINER_NAME="${PARSED_ARGS[0]:-}"
APPDATA_PATH="${PARSED_ARGS[1]:-}"
OUTPUT_DIR="${PARSED_ARGS[2]:-}"

# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
if [[ "$EUID" -ne 0 ]]; then
    error "Must be run as root"
    exit 1
fi

acquire_lock

if ! command -v docker &>/dev/null; then
    error "Docker command not found"
    exit 1
fi


# detect_hosts() sets MY_ID — used in summary
detect_hosts

# Arg validation
if [[ -z "$CONTAINER_NAME" || -z "$APPDATA_PATH" || -z "$OUTPUT_DIR" ]]; then
    error "Usage: container_data_export.sh <ContainerName> <appdata_path> <output_dir>"
    error "Example: container_data_export.sh Emby /mnt/media-servers/Media_Server/Emby /mnt/user/Backups/"
    exit 1
fi

if [[ ! -d "$APPDATA_PATH" ]]; then
    error "Appdata path not found: $APPDATA_PATH"
    exit 1
fi

if [[ ! -d "$OUTPUT_DIR" ]]; then
    error "Output directory not found: $OUTPUT_DIR"
    error "Create it first: mkdir -p \"$OUTPUT_DIR\""
    exit 1
fi

# Space check — conservative: appdata × 1.1
APPDATA_SIZE_KB=$(du -sk "$APPDATA_PATH" 2>/dev/null | cut -f1)
OUTPUT_FREE_KB=$(df "$OUTPUT_DIR" --output=avail 2>/dev/null | tail -1 | tr -d ' ')
REQUIRED_KB=$(( APPDATA_SIZE_KB * 11 / 10 ))
APPDATA_SIZE_H=$(du -sh "$APPDATA_PATH" 2>/dev/null | cut -f1)
OUTPUT_FREE_H=$(df -h "$OUTPUT_DIR" --output=avail 2>/dev/null | tail -1 | tr -d ' ')

if [[ "$OUTPUT_FREE_KB" -lt "$REQUIRED_KB" ]]; then
    error "Insufficient space in $OUTPUT_DIR"
    error "Estimated need: ~${APPDATA_SIZE_H} (×1.1 conservative) — available: ${OUTPUT_FREE_H}"
    exit 1
fi

log "Container: $CONTAINER_NAME"
log "Appdata:   $APPDATA_PATH ($APPDATA_SIZE_H)"
log "Output:    $OUTPUT_DIR ($OUTPUT_FREE_H free)"
log "Space check passed"

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"

# ── Ensure container is restarted on any exit if it was running ────────────────────────────────
CONTAINER_WAS_RUNNING=false
ARCHIVE_PATH=""

cleanup_on_exit() {
    local exit_code=$?
    # Remove partial archive on failure
    if [[ "$exit_code" -ne 0 && -n "$ARCHIVE_PATH" && -f "$ARCHIVE_PATH" ]]; then
        warn "Removing partial archive: $ARCHIVE_PATH"
        rm -f "$ARCHIVE_PATH" 2>/dev/null
    fi
    container_force_restart_if_needed "$CONTAINER_NAME" "$CONTAINER_WAS_RUNNING"
}
trap cleanup_on_exit EXIT

# ==============================================================================================
# ━━━ Stop Container ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_STOP Stop Container ━━━"

container_stop_for_maintenance "$CONTAINER_NAME" CONTAINER_WAS_RUNNING \
    "Stopping $CONTAINER_NAME for clean export..." log || exit 1
[[ "$CONTAINER_WAS_RUNNING" == false ]] && \
    echo "$CONTAINER_NAME is not running — archiving as-is (was stopped state respected)"

# ==============================================================================================
# ━━━ Archive ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_SYNC Archive ━━━"

TIMESTAMP=$(date '+%Y-%m-%d_%H-%M')
ARCHIVE_NAME="${CONTAINER_NAME}_${TIMESTAMP}.tar.gz"
ARCHIVE_PATH="${OUTPUT_DIR}/${ARCHIVE_NAME}"

warn "Creating: $ARCHIVE_PATH"
warn "Source:   $APPDATA_PATH ($APPDATA_SIZE_H)"

START=$(date +%s)
ARCHIVE_VERIFIED=false

if [[ "$DRY_RUN" == false ]]; then
    if tar -czf "$ARCHIVE_PATH" \
        -C "$(dirname "$APPDATA_PATH")" \
        "$(basename "$APPDATA_PATH")" 2>/dev/null; then

        ARCHIVE_SIZE=$(du -sh "$ARCHIVE_PATH" 2>/dev/null | cut -f1)
        warn "Archive created: $ARCHIVE_NAME ($ARCHIVE_SIZE)"

        # Verify archive integrity before declaring success
        log "Verifying archive..."
        if tar --test-label -f "$ARCHIVE_PATH" 2>/dev/null || \
           tar -tzf "$ARCHIVE_PATH" >/dev/null 2>&1; then
            echo "Archive verified ✅"
            ARCHIVE_VERIFIED=true
        else
            error "Archive verification FAILED — archive may be corrupt"
            error "Container will be restarted but DO NOT rely on this backup"
            notify "Container export archive corrupt — $CONTAINER_NAME backup may be unusable" \
                "Container Export" "warning"
        fi
    else
        error "tar failed — archive creation unsuccessful"
        exit 1
    fi
else
    warn "DRY RUN — would create: $ARCHIVE_PATH"
    ARCHIVE_VERIFIED=true
fi

END=$(date +%s)

# ==============================================================================================
# ━━━ Restart Container ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_START Restart Container ━━━"

container_restart_after_maintenance "$CONTAINER_NAME" "$CONTAINER_WAS_RUNNING" 3 "Container Export"

# Clear trap — clean exit, cleanup_on_exit no longer needed
trap - EXIT

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY CONTAINER EXPORT SUMMARY ━━━━━"
echo "$ICON_HOST        Identity:  $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_CONTAINERS  Container: $CONTAINER_NAME"
echo "$ICON_DISK        Appdata:   $APPDATA_PATH ($APPDATA_SIZE_H)"
echo "$ICON_SYNC        Archive:   ${ARCHIVE_NAME:-DRY RUN} ${ARCHIVE_SIZE:+($ARCHIVE_SIZE)}"
echo "$ICON_SHIELD      Verified:  $([[ "$ARCHIVE_VERIFIED" == true ]] && echo "✅" || echo "❌ FAILED")"
echo "$ICON_TIME        Duration:  $(format_duration $(( END - START )))"
echo ""

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — no changes made"
elif [[ "$ARCHIVE_VERIFIED" == true && "$RESTART_OK" == true ]]; then
    echo "$ICON_DONE Status: done — $ARCHIVE_NAME"
elif [[ "$ARCHIVE_VERIFIED" == false ]]; then
    echo "$ICON_ERROR Status: archive verification FAILED — check backup before relying on it"
else
    warn "Status: complete with warnings — check restart status above"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

[[ "$ARCHIVE_VERIFIED" == false ]] && exit 1
exit 0