#!/bin/bash

# Backup Dir
export SOURCE_DIR=$1
# Fix basename / showing up as "/" -> change to "root"
if [ $(basename $SOURCE_DIR) = / ]; then
    export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=root}
else
    export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=$(basename $SOURCE_DIR)}
fi
# Set snapshot prefix based on basename
export SNAPSHOT_TIME=$(date +"%y_%m_%d-%H.%M")
export SNAPSHOT_NAME=$SNAPSHOT_PREFIX-$SNAPSHOT_TIME
export SNAPSHOT_DIR=${SNAPSHOT_DIR:=/.snapshots}
export LATEST=$SNAPSHOT_PREFIX-latest

# For notifications
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ notifications.user.uid }}/bus
export USER={{ notifications.user.name }}

function exit_success {
    # Exit
    exit 0
}

function exit_fail {
    # Exit
    exit 1
}

function get_latest {
    DIR=$1
    if [ -f $DIR/$LATEST ]; then
        echo $(cat $DIR/$LATEST)
    else
        echo ""
    fi
}

function update_latest {
    DIR=$1
    NAME=$2
    echo $2 > $DIR/$LATEST
}

function log {
    LEVEL=$1
    MESSAGE=$2
    log_msg="$(date)    $LEVEL: $MESSAGE"
    echo $log_msg
}

function notify {
    LEVEL=$1
    MESSAGE=$2
    log "$LEVEL" "$MESSAGE"
    sudo -E -u $USER notify-send "$LEVEL" "$MESSAGE" --expire-time 10
}

function get_mount {
    findmnt -o target /dev/disk/by-uuid/$1 --noheadings
}

# Show snapshot settings
log "INFO" "SOURCE_DIR $SOURCE_DIR"
log "INFO" "SNAPSHOT_PREFIX $SNAPSHOT_PREFIX"
log "INFO" "SNAPSHOT_TIME $SNAPSHOT_TIME"
log "INFO" "SNAPSHOT_NAME $SNAPSHOT_NAME"
log "INFO" "SNAPSHOT_DIR $SNAPSHOT_DIR"
log "INFO" "LATEST $LATEST"

# Create readonly snapshot
log "INFO" "Creating snapshot from $SOURCE_DIR as $SNAPSHOT_DIR/$SNAPSHOT_NAME"
if [ -d $SNAPSHOT_DIR/$SNAPSHOT_NAME ]; then
    log "WARN" "Snapshot $SNAPSHOT_DIR/$SNAPSHOT_NAME already created. Skipping"
else
    btrfs subvolume snapshot -r $SOURCE_DIR $SNAPSHOT_DIR/$SNAPSHOT_NAME
fi

# Update latest in snapshot dir
log "INFO" "Updating latest in $SNAPSHOT_DIR to $SNAPSHOT_NAME."
update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME

export BACKUP_DRIVE_UUID={{ disk.uuid }}
get_mount $BACKUP_DRIVE_UUID
if [ $? != 0 ]; then
    notify "WARN" "Snapshot complete but backup drive $BACKUP_DRIVE_UUID not mounted. Backup not sent."
    exit 1
fi

export BACKUP_DRIVE_MNT=$(findmnt -o target /dev/disk/by-uuid/$BACKUP_DRIVE_UUID --noheadings)
export BACKUP_DIR=$BACKUP_DRIVE_MNT/$(hostname)
mkdir -p $BACKUP_DIR
log "INFO" "BACKUP_DRIVE_MNT $BACKUP_DRIVE_MNT"
log "INFO" "BACKUP_DIR $BACKUP_DIR"
log "INFO" "Drive $BACKUP_DRIVE_UUID mounted at $BACKUP_DRIVE_MNT"

# First check if the snapshot dir has a "latest" snapshot
# This will be needed to send an incremental snapshot
LATEST_SNAPSHOT="$(get_latest $SNAPSHOT_DIR)"
log "INFO" "Latest snapshot is $LATEST_SNAPSHOT"

# Next, check if the backup drive has a "latest" snapshot
LATEST_BACKUP="$(get_latest $BACKUP_DIR)"
log "INFO" "Latest backup is $LATEST_BACKUP"

# Now check if the "latest" snapshots match
# btrfs requires both the sending drive and receiving drive have
# matching parent snapshots.
#
# There are a few scenarios to cover
# 1. Neither the backup drive nor the local snapshot dir have a "latest"
#       This can happen if the backup occurs before any snapshots are
#       taken. Don't send anything.
# 2. The backup drive has a "latest" but the snapshot dir doesn't
#       This can happen when the local drive is restored from backup
#       but the snapshot dir didn't copy over. nothing to send.
# 3. The backup drive and snapshot dir have a "latest" and they are the
#    same.
#       Send backup with parent as normal.
# 4. The snapshot dir has a "latest" but the backup drive doesn't
#       This can happen when backing up for the first time. Send the
#       snapshot without a parent
# 5. Both the snapshot dir and backup drive have a latest, but they are
#    out of sync.
#       This can happen when snapshots are taken with the backup drive
#       disconnected. There's a few sub-scenarios here:
#   a. The snapshot dir has the "latest" snapshot from the backup dir,
#      it's just older than the "latest" snapshot in the snapshot dir
#           Re-sync the "latest" snapshot dir with the one in the
#           backup dir. Send as normal with parents.
#   b. The snapshot dir does not have the "latest" snapshot from the
#      backup dir. 
#           Here be dragons. Something went wrong and will likely need
#           to be manually reconfigured. Raise a critical alert.

# Scenario 1 and 2
if [ "$LATEST_SNAPSHOT" = "" ]; then
    notify "WARN" "Neither the snapshot dir nor the backup drive has a 'latest' snapshot."
    exit_success
fi

# Scenario 3
if [ "$LATEST_SNAPSHOT" = "$LATEST_BACKUP" ]; then
    log "INFO" "Proceeding with backups as normal."
    # Send incremental snapshot
    btrfs send -p $SNAPSHOT_DIR/$LATEST_SNAPSHOT $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
    if [ $? != 0 ]; then
        notify "ERROR" "btrfs send -p $SNAPSHOT_DIR/$LATEST_SNAPSHOT $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
        exit_fail
    fi

    # Update latest in backup dir
    update_latest $BACKUP_DIR $SNAPSHOT_NAME

    # Update latest in snapshot dir
    update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME

    # Exit
    notify "INFO" "Backup completed" "Backup $SNAPSHOT_NAME completed successfully."
    exit_success
fi

# Scenario 4
if [ "$LATEST_BACKUP" = "" ]; then
    log "INFO" "No prior backups detected. Sending full backup."
    # Send incremental snapshot
    btrfs send $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
    if [ $? != 0 ]; then
        notify "ERROR" "btrfs send $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
        exit_fail
    fi

    # Update latest in backup dir
    update_latest $BACKUP_DIR $SNAPSHOT_NAME

    # Update latest in snapshot dir
    update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME

    # Exit
    notify "INFO" "Backup $SNAPSHOT_NAME completed successfully."
    exit_success
fi

# Scenario 5a
log "INFO" "Detected drift. Attempting to synchronize latest snapshot with backup. Set to $LATEST_BACKUP."
if [ -d $SNAPSHOT_DIR/$LATEST_BACKUP ]; then
    log "INFO" "$LATEST_BACKUP found in snapshot dir. Synchronizing and proceeding."

    btrfs send -p $SNAPSHOT_DIR/$LATEST_BACKUP $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR
    if [ $? != 0 ]; then
        notify "ERROR" "btrfs send -p $SNAPSHOT_DIR/$LATEST_BACKUP $SNAPSHOT_DIR/$SNAPSHOT_NAME failed."
        exit_fail
    fi

    # Update latest in backup dir
    update_latest $BACKUP_DIR $SNAPSHOT_NAME

    # Update latest in snapshot dir
    update_latest $SNAPSHOT_DIR $SNAPSHOT_NAME

    # Exit
    notify "INFO" "Backup $SNAPSHOT_NAME completed successfully."
    exit_success
# Scenario 5b
else
    log "ERROR" "Something went wrong. $LATEST_BACKUP not found in $SNAPSHOT_DIR."
    notify "ERROR" "$LATEST_BACKUP not found in $SNAPSHOT_DIR."
    exit_fail
fi
