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

CONFIG_URL_DEFAULT="https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/thunderbird-backup.conf"
CONFIG_FILE="${THUNDERBIRD_BACKUP_CONFIG:-}"
CONFIG_URL="${THUNDERBIRD_BACKUP_CONFIG_URL:-$CONFIG_URL_DEFAULT}"
SHOW_HELP=false

args=("$@")
for ((i = 0; i < ${#args[@]}; i++)); do
  case "${args[$i]}" in
    --config)
      CONFIG_FILE="${args[$((i + 1))]:?Missing value for --config}"
      CONFIG_URL=""
      ;;
    --config-url)
      CONFIG_URL="${args[$((i + 1))]:?Missing value for --config-url}"
      CONFIG_FILE=""
      ;;
    -h|--help)
      SHOW_HELP=true
      ;;
  esac
done

SOURCE_DEFAULT="/home/vernon/.thunderbird/"
DESTINATION_DEFAULT="/mnt/Synology/Backup/Applications/Thunderbird/"
MOUNTPOINT_DEFAULT="/mnt/Synology/Backup"
LOG_FILE_DEFAULT="/home/vernon/.local/state/thunderbird-backup.log"
LOCK_FILE_DEFAULT="/home/vernon/.cache/thunderbird-backup.lock"
DRY_RUN=false

SOURCE="$SOURCE_DEFAULT"
DESTINATION="$DESTINATION_DEFAULT"
MOUNTPOINT="$MOUNTPOINT_DEFAULT"
LOG_FILE="$LOG_FILE_DEFAULT"
LOCK_FILE="$LOCK_FILE_DEFAULT"

trim() {
  local value="$1"
  value="${value#"${value%%[![:space:]]*}"}"
  value="${value%"${value##*[![:space:]]}"}"
  printf '%s' "$value"
}

parse_config_content() {
  local line key raw value

  while IFS= read -r line || [[ -n "$line" ]]; do
    line="$(trim "$line")"
    [[ -z "$line" || "$line" == \#* ]] && continue

    if [[ ! "$line" =~ ^([A-Z_]+)[[:space:]]*=(.*)$ ]]; then
      echo "ERROR: Invalid config line: $line" >&2
      exit 2
    fi

    key="${BASH_REMATCH[1]}"
    raw="$(trim "${BASH_REMATCH[2]}")"

    if [[ "$raw" == \"*\" && "$raw" == *\" ]]; then
      value="${raw:1:${#raw}-2}"
    elif [[ "$raw" == \'*\' && "$raw" == *\' ]]; then
      value="${raw:1:${#raw}-2}"
    else
      value="$raw"
    fi

    case "$key" in
      SOURCE) SOURCE="$value" ;;
      DESTINATION) DESTINATION="$value" ;;
      MOUNTPOINT) MOUNTPOINT="$value" ;;
      LOG_FILE) LOG_FILE="$value" ;;
      LOCK_FILE) LOCK_FILE="$value" ;;
      *)
        echo "ERROR: Unsupported config key: $key" >&2
        exit 2
        ;;
    esac
  done
}

usage() {
  cat <<'USAGE'
Usage: backup-thunderbird-to-synology.sh [options]

Options:
  --source PATH       Thunderbird profile directory to back up
  --destination PATH  Backup destination directory
  --mountpoint PATH   Required mounted path to check before backup
  --no-mount-check    Disable the mountpoint safety check
  --log-file PATH     Log file path
  --config PATH       Local config file path; disables remote config
  --config-url URL    Remote config URL
  --dry-run           Show what would change without writing files
  -h, --help          Show this help

Config defaults to:
  https://opengist.resetrix.work/weehong/2de15ba0106a475fa41215159203a63b/raw/HEAD/thunderbird-backup.conf

Supported config variables: SOURCE, DESTINATION, MOUNTPOINT, LOG_FILE, LOCK_FILE.
Environment overrides: THUNDERBIRD_BACKUP_SOURCE, THUNDERBIRD_BACKUP_DESTINATION,
THUNDERBIRD_BACKUP_MOUNTPOINT, THUNDERBIRD_BACKUP_LOG_FILE, THUNDERBIRD_BACKUP_LOCK_FILE,
THUNDERBIRD_BACKUP_CONFIG, THUNDERBIRD_BACKUP_CONFIG_URL.
USAGE
}

if [[ "$SHOW_HELP" == true ]]; then
  usage
  exit 0
fi

if [[ -n "$CONFIG_FILE" ]]; then
  if [[ ! -f "$CONFIG_FILE" ]]; then
    mkdir -p "$(dirname "$LOG_FILE")"
    echo "$(date -Is) ERROR: Config file not found: $CONFIG_FILE" >> "$LOG_FILE"
    exit 1
  fi
  parse_config_content < "$CONFIG_FILE"
elif [[ -n "$CONFIG_URL" ]]; then
  config_content="$(curl -fsSL --max-time 20 "$CONFIG_URL")" || {
    mkdir -p "$(dirname "$LOG_FILE")"
    echo "$(date -Is) ERROR: Unable to fetch config from $CONFIG_URL" >> "$LOG_FILE"
    exit 1
  }
  parse_config_content <<< "$config_content"
fi

SOURCE="${THUNDERBIRD_BACKUP_SOURCE:-$SOURCE}"
DESTINATION="${THUNDERBIRD_BACKUP_DESTINATION:-$DESTINATION}"
MOUNTPOINT="${THUNDERBIRD_BACKUP_MOUNTPOINT:-$MOUNTPOINT}"
LOG_FILE="${THUNDERBIRD_BACKUP_LOG_FILE:-$LOG_FILE}"
LOCK_FILE="${THUNDERBIRD_BACKUP_LOCK_FILE:-$LOCK_FILE}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --source)
      SOURCE="${2:?Missing value for --source}"
      shift 2
      ;;
    --destination)
      DESTINATION="${2:?Missing value for --destination}"
      shift 2
      ;;
    --mountpoint)
      MOUNTPOINT="${2:?Missing value for --mountpoint}"
      shift 2
      ;;
    --no-mount-check)
      MOUNTPOINT=""
      shift
      ;;
    --log-file)
      LOG_FILE="${2:?Missing value for --log-file}"
      shift 2
      ;;
    --dry-run)
      DRY_RUN=true
      shift
      ;;
    --config)
      shift 2
      ;;
    --config-url)
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "ERROR: Unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$LOCK_FILE")"

timestamp() {
  date -Is
}

normalize_path() {
  realpath -m "$1"
}

SOURCE_REAL="$(normalize_path "$SOURCE")"
DESTINATION_REAL="$(normalize_path "$DESTINATION")"

if [[ "$SOURCE" != /* || "$DESTINATION" != /* || "$LOG_FILE" != /* || "$LOCK_FILE" != /* ]]; then
  echo "$(timestamp) ERROR: SOURCE, DESTINATION, LOG_FILE, and LOCK_FILE must be absolute paths." >> "$LOG_FILE"
  exit 1
fi

if [[ -n "$MOUNTPOINT" && "$MOUNTPOINT" != /* ]]; then
  echo "$(timestamp) ERROR: MOUNTPOINT must be an absolute path." >> "$LOG_FILE"
  exit 1
fi

if [[ "$SOURCE_REAL" == "$DESTINATION_REAL" ]]; then
  echo "$(timestamp) ERROR: SOURCE and DESTINATION cannot be the same path: $SOURCE_REAL" >> "$LOG_FILE"
  exit 1
fi

if [[ "$DESTINATION_REAL" == "$SOURCE_REAL"/* ]]; then
  echo "$(timestamp) ERROR: DESTINATION cannot be inside SOURCE: $DESTINATION_REAL" >> "$LOG_FILE"
  exit 1
fi

case "$DESTINATION_REAL" in
  /|/home|/home/vernon|/mnt|/mnt/Synology|/mnt/Synology/Backup)
    echo "$(timestamp) ERROR: Refusing unsafe backup destination: $DESTINATION_REAL" >> "$LOG_FILE"
    exit 1
    ;;
esac

if [[ -n "$MOUNTPOINT" ]] && ! mountpoint -q "$MOUNTPOINT"; then
  echo "$(timestamp) ERROR: $MOUNTPOINT is not mounted; backup skipped." >> "$LOG_FILE"
  exit 1
fi

if [[ ! -d "$SOURCE" ]]; then
  echo "$(timestamp) ERROR: Thunderbird profile directory not found: $SOURCE" >> "$LOG_FILE"
  exit 1
fi

mkdir -p "$DESTINATION"

exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  echo "$(timestamp) INFO: Previous Thunderbird backup still running; backup skipped." >> "$LOG_FILE"
  exit 0
fi

{
  if [[ "$DRY_RUN" == true ]]; then
    echo "$(timestamp) INFO: Starting Thunderbird backup dry run: $SOURCE_REAL -> $DESTINATION_REAL"
  else
    echo "$(timestamp) INFO: Starting Thunderbird backup: $SOURCE_REAL -> $DESTINATION_REAL"
  fi

  rsync_args=(-a --delete)
  if [[ "$DRY_RUN" == true ]]; then
    rsync_args+=(-n --itemize-changes)
  fi

  rsync "${rsync_args[@]}" \
    --exclude='*.parentlock' \
    --exclude='lock' \
    "$SOURCE" "$DESTINATION"
  if [[ "$DRY_RUN" == true ]]; then
    echo "$(timestamp) INFO: Thunderbird backup dry run completed."
  else
    echo "$(timestamp) INFO: Thunderbird backup completed."
  fi
} >> "$LOG_FILE" 2>&1
