#!/bin/bash

SCRIPT="$(cd "${0%/*}" && echo "$PWD")/${0##*/}"
SCRIPT="$(readlink -f "$SCRIPT")"
SCRIPT_DIR="$(dirname "$SCRIPT")"

: ${SOURCE_DIR:="/srv/www/htdocs/opensuse"}
: ${SNAPSHOT_DIR:="/srv/www/htdocs/snapshot"}
: ${RSYNC_INCLUDE:="$SCRIPT_DIR/rsync/include-tumbleweed.txt"}
: ${SNAPSHOT_AGE_MAX:="-180 days"}
: ${SNAPSHOT_COUNT_MAX:="50"}
: ${SNAPSHOT_REDIRECT_EXPIRE:="360"}

SNAPSHOT_OLDEST=$(date -d "$SNAPSHOT_AGE_MAX" +%Y%m%d)
LOCKFILE_NAME="$SNAPSHOT_DIR/.lock"

snapshot_dir_init()
{
  mkdir "$SNAPSHOT_DIR"
  echo 0 > "$SNAPSHOT_DIR/latest"
  echo -n > "$SNAPSHOT_DIR/list"
}

snapshot_check()
{
  echo -n "$snapshot: "
  if [ "$snapshot" == "${snapshots[0]}" ] ; then
    echo "already snapshotted"
    snapshot_check_redirect
    exit
  elif [ "$snapshot" == "" ] ; then
    echo "empty snapshot, exit."
    exit 1
  fi
  echo "new"
}

snapshot_check_redirect()
{
  local snapshot_latest="$SNAPSHOT_DIR/$snapshot"
  if test $(find "$snapshot_latest/.htaccess" -mmin +"$SNAPSHOT_REDIRECT_EXPIRE") ; then
    rm "$snapshot_latest/.htaccess"
    echo "$snapshot: redirect removed"
  fi
}

snapshot_create()
{
  # Snapshot latest (assumes source are fully updated).
  local snapshot_latest="$SNAPSHOT_DIR/$snapshot"
  local snapshot_older="$SNAPSHOT_DIR/${snapshots[0]}"

  if [ ${#snapshots[@]} -gt 0 ] && [ -d "$snapshot_older" ] ; then
    # Remove redirect and serve directly.
    if [ -f "$snapshot_older/.htaccess" ] ; then
      rm "$snapshot_older/.htaccess"
    fi
    echo "${snapshots[0]}: redirect removed"

    # Hardlink against previous snapshot.
    rsync --include-from "$RSYNC_INCLUDE" -a --delete --link-dest "$(realpath $snapshot_older)" "$SOURCE_DIR/" "$snapshot_latest"

    snapshot_create_rpm_list "$snapshot_latest"
    comm --check-order -23 "$snapshot_latest/rpm.list" "$snapshot_older/rpm.list" > "$snapshot_latest/rpm.unique.list"

    echo "$snapshot: created against ${snapshots[0]}"
  else
    # First snapshot, with no previous base (ie make full copy).
    rsync --include-from "$RSYNC_INCLUDE" -a --delete "$SOURCE_DIR/" "$snapshot_latest"

    snapshot_create_rpm_list "$snapshot_latest"
    cp "$snapshot_latest/rpm.list" "$snapshot_latest/rpm.unique.list"

    echo "$snapshot: created"
  fi

  snapshot_create_disk "$snapshot_latest"

  # Redirect to main mirrors (especially while creating) until next snapshot.
  cp "$SCRIPT_DIR/.htaccess" "$snapshot_latest"
}

snapshot_create_rpm_list()
{
  local snapshot_latest="$1"
  rsync --recursive --list-only --include-from "$RSYNC_INCLUDE" "$snapshot_latest/" | \
    grep -oP "[^ ]+\.rpm$" | sort -n > "$snapshot_latest/rpm.list"
}

snapshot_create_disk()
{
  local snapshot_latest="$1"
  (
    cd "$snapshot_latest"
    cat "rpm.unique.list" | tr '\n' '\0' | du -csb --files0-from=- | tail -n 1
    wc -l "rpm.unique.list"
  ) > "$snapshot_latest/disk"
}

snapshot_clean()
{
  echo "cleaning snapshots (oldest: $SNAPSHOT_OLDEST, max: $SNAPSHOT_COUNT_MAX)..."
  local index=1
  local snap
  for snap in ${snapshots[@]} ; do
    if ( [ $snap -lt $SNAPSHOT_OLDEST ] || [ $index -ge $SNAPSHOT_COUNT_MAX ] ) ; then
      snapshots=(${snapshots[@]/$snap})
      snapshot_remove "$snap"
    fi
    ((index++))
  done
}

snapshot_remove()
{
  rm -rf "$SNAPSHOT_DIR/$1"
  echo "$1: removed"
}


if [ ! -d "$SNAPSHOT_DIR" ] || [ ! -f "$SNAPSHOT_DIR/latest" ] ; then
  snapshot_dir_init
fi

if [ ! -z "$SOURCE_LOCK" ] && [ -f "$SOURCE_LOCK" ] ; then
  echo "exiting due to source lock at $SOURCE_LOCK"
  exit
fi

lockfile -r 0 "$LOCKFILE_NAME" || exit 1
trap "rm -f \"$LOCKFILE_NAME\"; exit" 0 1 2 3 9 15

snapshots=($(find "$SNAPSHOT_DIR" -mindepth 1 -maxdepth 1 -type d | grep -oP "\d+$" | sort -r))
snapshot=$(grep -oP " \K\d+" "$SOURCE_DIR/tumbleweed/repo/oss/media.1/products")

snapshot_check
snapshot_create
snapshot_clean

echo "updating lastest and list files..."
echo "$snapshot" > "$SNAPSHOT_DIR/latest"
echo "$snapshot" > "$SNAPSHOT_DIR/list"
if [ ${#snapshots[@]} -gt 0 ] ; then
  printf '%s\n' "${snapshots[@]}" >> "$SNAPSHOT_DIR/list"
fi

(
  du -csb "$SNAPSHOT_DIR" | tail -n 1
  rsync --recursive --list-only "$SNAPSHOT_DIR/" | wc -l
) > "$SNAPSHOT_DIR/disk"
