#!/usr/bin/env bash
set -euo pipefail

# rollback.sh — Restore system config from tunectl backup
#
# Usage:
#   rollback.sh [--list]
#   rollback.sh [--backup <timestamp>]
#
# Modes:
#   --list              Show available backups (works without root)
#   (default, no args)  Restore from most recent backup (requires root)
#   --backup <ts>       Restore from specific backup timestamp (requires root)
#
# Environment overrides (for testing):
#   BACKUP_ROOT              Override backup directory path
#   TUNECTL_SYSROOT          Prefix for restore destination paths (sandbox)
#   TUNECTL_SKIP_ROOT_CHECK  Skip the root privilege check (set to 1)
#   TUNECTL_SKIP_SYSCTL_RELOAD  Skip sysctl --system reload (set to 1)
#
# Exit codes: 0=success, 1=operational failure, 2=usage error

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_ROOT="${BACKUP_ROOT:-/var/lib/tunectl/backups}"

# -------------------------------------------------------
# Usage / help
# -------------------------------------------------------
usage() {
  cat >&2 <<EOF
Usage: rollback.sh [--list] [--backup <timestamp>]

Options:
  --list              List available backups (does not require root)
  --backup <ts>       Restore from a specific backup timestamp
  (no args)           Restore from the most recent backup
  --help              Show this help message

Exit codes:
  0  Success
  1  Operational failure
  2  Usage error
EOF
  exit 2
}

# -------------------------------------------------------
# Globals
# -------------------------------------------------------
MODE=""
BACKUP_TIMESTAMP=""

# -------------------------------------------------------
# Parse arguments
# -------------------------------------------------------
parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --list)
        MODE="list"
        shift
        ;;
      --backup)
        [[ $# -lt 2 ]] && { echo "Error: --backup requires a timestamp value" >&2; usage; }
        MODE="restore"
        BACKUP_TIMESTAMP="$2"
        shift 2
        ;;
      --help|-h)
        usage
        ;;
      *)
        echo "Error: Unknown argument: $1" >&2
        usage
        ;;
    esac
  done

  # Default mode: restore from latest
  if [[ -z "$MODE" ]]; then
    MODE="restore"
  fi
}

# -------------------------------------------------------
# List available backup directories, sorted newest-first
# -------------------------------------------------------
list_backups() {
  if [[ ! -d "$BACKUP_ROOT" ]] || [[ -z "$(ls -A "$BACKUP_ROOT" 2>/dev/null)" ]]; then
    echo "No backups found in $BACKUP_ROOT"
    return 0
  fi

  echo "Available backups (newest first):"
  echo ""

  # List directories sorted newest-first by name (timestamps sort lexicographically)
  local backup_dirs
  backup_dirs=$(find "$BACKUP_ROOT" -mindepth 1 -maxdepth 1 -type d | sort -r)

  if [[ -z "$backup_dirs" ]]; then
    echo "No backups found in $BACKUP_ROOT"
    return 0
  fi

  while IFS= read -r dir; do
    local ts
    ts=$(basename "$dir")
    local file_count
    file_count=$(find "$dir" -type f | wc -l)
    echo "  $ts  ($file_count files)"
  done <<< "$backup_dirs"

  echo ""
}

# -------------------------------------------------------
# Require root for restore operations
# -------------------------------------------------------
require_root() {
  if [[ "${TUNECTL_SKIP_ROOT_CHECK:-0}" == "1" ]]; then
    return 0
  fi
  if [[ $EUID -ne 0 ]]; then
    echo "Error: Restore requires root privileges. Run with sudo." >&2
    exit 1
  fi
}

# -------------------------------------------------------
# Resolve which backup to use
# Returns the full path to the backup directory
# -------------------------------------------------------
resolve_backup_dir() {
  # Check if backup root exists at all
  if [[ ! -d "$BACKUP_ROOT" ]] || [[ -z "$(ls -A "$BACKUP_ROOT" 2>/dev/null)" ]]; then
    echo "Error: No backups found in $BACKUP_ROOT" >&2
    exit 1
  fi

  local backup_dirs
  backup_dirs=$(find "$BACKUP_ROOT" -mindepth 1 -maxdepth 1 -type d | sort -r)

  if [[ -z "$backup_dirs" ]]; then
    echo "Error: No backups found in $BACKUP_ROOT" >&2
    exit 1
  fi

  if [[ -n "$BACKUP_TIMESTAMP" ]]; then
    # Specific backup requested
    local target_dir="${BACKUP_ROOT}/${BACKUP_TIMESTAMP}"
    if [[ ! -d "$target_dir" ]]; then
      echo "Error: Backup '$BACKUP_TIMESTAMP' not found." >&2
      echo "" >&2
      echo "Available backups:" >&2
      while IFS= read -r dir; do
        echo "  $(basename "$dir")" >&2
      done <<< "$backup_dirs"
      exit 1
    fi
    echo "$target_dir"
  else
    # Use most recent backup (first in sorted list)
    echo "$backup_dirs" | head -1
  fi
}

# -------------------------------------------------------
# Restore files from a backup directory
# -------------------------------------------------------
do_restore() {
  local backup_dir
  backup_dir=$(resolve_backup_dir)

  local ts
  ts=$(basename "$backup_dir")

  # Verify backup is not empty
  local file_count
  file_count=$(find "$backup_dir" -type f | wc -l)

  if [[ $file_count -eq 0 ]]; then
    echo "Error: Backup '$ts' exists but contains no files." >&2
    exit 1
  fi

  local sysroot="${TUNECTL_SYSROOT:-}"
  local restore_failed=false

  echo "============================================"
  echo "  tunectl rollback — Restoring from: $ts"
  echo "============================================"
  echo ""
  echo "Backup contains $file_count file(s)"
  echo ""

  local restored=0
  local has_sysctl=false

  # Find all files in the backup directory and restore them
  while IFS= read -r backup_file; do
    # Compute the relative path from the backup dir (this is the original absolute path)
    local rel_path="${backup_file#"$backup_dir"}"
    local dest_path="${sysroot}${rel_path}"

    # Create destination directory if needed
    mkdir -p "$(dirname "$dest_path")"

    # Copy the backed-up file to its original path
    cp -p "$backup_file" "$dest_path"
    echo "  Restored: $rel_path"
    restored=$((restored + 1))

    # Track if any sysctl config was restored
    if echo "$rel_path" | grep -q "sysctl"; then
      has_sysctl=true
    fi
  done < <(find "$backup_dir" -type f | sort)

  echo ""

  # Reload sysctl if sysctl config files were restored (VAL-ROLL-007)
  if $has_sysctl; then
    if [[ "${TUNECTL_SKIP_SYSCTL_RELOAD:-0}" == "1" ]]; then
      echo "Sysctl reload: skipped (test mode)"
    else
      echo "Reloading sysctl values..."
      if sysctl --system >/dev/null 2>&1; then
        echo "  Sysctl values reloaded successfully"
      else
        echo "  Error: sysctl --system failed after restore" >&2
        restore_failed=true
      fi
    fi
    echo ""
  fi

  echo "============================================"
  echo "  Rollback complete — $restored file(s) restored"
  echo "  Backup preserved: $backup_dir"
  echo "============================================"

  if $restore_failed; then
    exit 1
  fi
}

# -------------------------------------------------------
# Main
# -------------------------------------------------------
main() {
  parse_args "$@"

  case "$MODE" in
    list)
      list_backups
      ;;
    restore)
      require_root
      do_restore
      ;;
  esac

  exit 0
}

main "$@"
