#!/bin/bash

dir=$1
dir_path=$2
commitlog_param=$3
commitlog_path=$4
rf_param=$5
rf=$6
cassandra_param=$7
cassandra_dir=$8

osname=$(uname -o)

wait_s=30

user_name=cassandra
password=cassandra

COMMITLOG_DIR=$commitlog_path
DATABASE_DIR=$cassandra_database
CASSANDRA_PATH=$cassandra
CASSANDRA_AS_SERVICE=$service
RECOVERY_FILE=$rf

init() {
	init_database_dir
	init_commitlog_dir
	init_cassandra_home
	init_recovery_file
}

init_database_dir() {
	if [ ! -d "DATABASE_DIR" ]; then
		if [ "$dir" = "-dir" ] ; then
			if [ ! -d "$dir_path" ]; then
				request_db_dir
			else
				DATABASE_DIR=$dir_path
			fi
		else
			request_db_dir
		fi
	fi
}

init_commitlog_dir() {
	if [ ! -d "COMMITLOG_DIR" ]; then
		if [ "$commitlog_param" = "-commitlog" ] ; then
			if [ ! -d "$commitlog_path" ]; then
				request_commitlog_dir
			else
				COMMITLOG_DIR=$commitlog_path
			fi
		else
			request_commitlog_dir
		fi
	fi
}

init_recovery_file() {
	if [ ! -f "RECOVERY_FILE" ]; then
		if [ "$rf_param" = "-rf" ] ; then
			if [ ! -f "$rf" ]; then
				request_recovery_file
			else
				RECOVERY_FILE=$rf
			fi
		else
			request_recovery_file
		fi
	fi
}

init_cassandra_home() {
	if [ "$CASSANDRA_AS_SERVICE" = false ] ; then 
		if [ ! -d "CASSANDRA_PATH" ]; then
			if [ "$cassandra_param" = "-cassandra" ] ; then
				if [ ! -d "$cassandra_dir" ]; then
					request_cassandra_home
				else
					CASSANDRA_PATH=$cassandra_dir
				fi
			else
				request_cassandra_home
			fi
		fi
	fi
}

remove_old_data() {
	echo "Removing current data *** DESTRUCTIVE ***" 
	(cd "$DATABASE_DIR/data/"
		find . -type f ! -path "*/snapshots/*" -exec rm -f {} \;
		check_for_errors
		find . -type d ! -path "*/snapshots/*" -iname "*_Idx" -exec rm -rf {} \;
		check_for_errors
	)
}

stop_cassandra() {
	if [ "$CASSANDRA_AS_SERVICE" = '' ] || [ "$CASSANDRA_AS_SERVICE" = true ] ; then 
		echo "Stoping Cassandra service"
		if [ "$osname" = "Cygwin" ] ; then 
			net stop DataStax_Cassandra_Community_Server
		else
			systemctl stop cassandra
			echo "Waiting $wait_s seconds for Cassandra to stop"
			sleep $wait_s
			nohup ps aux | grep Cassandra | awk {'print $2'} | xargs kill &
			check_for_errors
		fi
	else
		echo "Stoping Cassandra process"
		if [ "$osname" = "Cygwin" ] ; then 
			wmic PROCESS where "CommandLine like '%cassandra%'" Call Terminate
		else
			nohup ps aux | grep Cassandra | awk {'print $2'} | xargs kill &
			check_for_errors
		fi
	fi
	sleep 5
}

start_cassandra() {
	if [ "$CASSANDRA_AS_SERVICE" = '' ] || [ "$CASSANDRA_AS_SERVICE" = true ] ; then 
		echo "Starting Cassandra service"
		if [ "$osname" = "Cygwin" ] ; then 
			net start DataStax_Cassandra_Community_Server
		else
			systemctl start cassandra
		fi
	else
		if [ -d $CASSANDRA_PATH ]; then
			echo "Starting Cassandra process"
			if [ "$osname" = "Cygwin" ] ; then 
				(cd "$CASSANDRA_PATH/bin/"
					cassandra -f & disown
					check_for_errors
				)
			else
				(cd "$CASSANDRA_PATH/bin/"
					./cassandra
					check_for_errors
				)
			fi
		else
			echo "Cassandra home set to: $CASSANDRA_PATH"
			echo "Cassandra home is not set correctly. Example usage: $ cassandra=/opt/cassandra-2.2.5 ./restore_single_node.sh"
			echo "Or you can start Cassandra manually"
		fi
	fi
	echo "Waiting $wait_s seconds for Cassandra"
	sleep $wait_s
}

restore_data() {
	echo "Restoring snapshot data"
	(cd "$DATABASE_DIR/data/"
		for keyspace in `find . -mindepth 2 -maxdepth 2 -type d `; do
			echo "Restoring: $keyspace"
			for snapshot in `find $keyspace -mindepth 2 -maxdepth 2 -type d | sort -nr | head -1`; do
				echo "Copying contents from: $snapshot to: $keyspace"
				find $snapshot -type f ! -path "./esi/*/snapshots/*_Idx*" -exec cp -p {} $keyspace \;
				check_for_errors
				find $snapshot -type d -path "./esi/*/snapshots/*_Idx*" -exec cp -rp {} $keyspace \;
				check_for_errors
			done
		done
	)

	if [ "$CASSANDRA_AS_SERVICE" = '' ] || [ "$CASSANDRA_AS_SERVICE" = true ]; then
		(cd "$DATABASE_DIR"
			if [ ! "$osname" = "Cygwin" ]; then
				chown -R cassandra:cassandra data/
			else
				chown -R Administrators:SYSTEM data/
			fi
		)
	fi
}

clear_commitlog() {
	(cd "$DATABASE_DIR"
		if ls CommitLog* 1> /dev/null 2>&1; then
			commitlog_found
			(cd "$COMMITLOG_DIR"
				rm -f CommitLog*
			)
			check_for_errors
		else
			if [ ! -d "commitlog" ]; then
				(cd "/var/lib/cassandra"
					if [ ! -d "commitlog" ]; then
						commitlog_not_found
					else
						commitlog_found
						(cd "$COMMITLOG_DIR"
							rm -f CommitLog*
							check_for_errors
						)
					fi
				)
			else
				commitlog_found
				(cd "$COMMITLOG_DIR"
					rm -f CommitLog*
					check_for_errors
				)
			fi
		fi
	)
}

commitlog_not_found() {
	echo "Commitlog dir not cleared, please clear it manually"
}

commitlog_found() {
	echo "Removing old commit logs"
}

repair_keyspaces_linux() {
	echo "Repairing cassandra keyspaces"
	sleep $wait_s
	nodetool -h localhost -u $user_name -pw $password -p 7199 repair esi
	nodetool -h localhost -u $user_name -pw $password -p 7199 repair system
	nodetool -h localhost -u $user_name -pw $password -p 7199 repair system_auth
	nodetool -h localhost -u $user_name -pw $password -p 7199 repair system_distributed
	nodetool -h localhost -u $user_name -pw $password -p 7199 repair system_traces
	check_for_errors
}

repair_keyspaces_cygwin() {
	echo "Repairing Cassandra keyspaces"
	nodetool.bat -h localhost -u $user_name -pw $password -p 7199 repair esi
	nodetool.bat -h localhost -u $user_name -pw $password -p 7199 repair system
	nodetool.bat -h localhost -u $user_name -pw $password -p 7199 repair system_auth
	nodetool.bat -h localhost -u $user_name -pw $password -p 7199 repair system_distributed
	nodetool.bat -h localhost -u $user_name -pw $password -p 7199 repair system_traces
	check_for_errors
}

remove_recovery() {
	echo "Removing old snapshots"
	(cd "$DATABASE_DIR/data"
		nohup find . -path "*/snapshots/*" -exec rm -rf {} \; &
		check_for_errors
	)
}

request_recovery_file() {
	need_file=true
	while $need_file; do
    	read -p "Please enter full backup file path. I.e. /home/user/backups/cassandra_backup_2016.07.29.12.23.24.tar: " RECOVERY_FILE
		if [ ! -f "$RECOVERY_FILE" ]; then
			echo "No backup file found $RECOVERY_FILE"
			echo "Wrong file entered."
		else
			need_file=false;
		fi
	done
}

request_cassandra_home() {
	need_dir=true
	while $need_dir; do
    	read -p "Please enter Cassandra home directory. I.e. /opt/cassandra-2.2.5: " CASSANDRA_PATH
		if [ ! -d "$CASSANDRA_PATH/bin/" ]; then
			echo "No directory $CASSANDRA_PATH/bin/"
			echo "Wrong directory entered."
		else
			need_dir=false;
		fi
	done
}

request_commitlog_dir() {
	need_dir=true
	while $need_dir; do
    	read -p "Please enter Cassandra commitlog directory. I.e. /var/lib/cassandra/commitlog: " COMMITLOG_DIR
		if [ ! -d "$COMMITLOG_DIR" ]; then
			echo "No directory $COMMITLOG_DIR"
			echo "Wrong directory entered."
		else
			need_dir=false;
		fi
	done
}

request_db_dir() {
	need_dir=true	
	while $need_dir; do
    	read -p "Please enter Cassandra database directory. I.e. /var/lib/cassandra: " DATABASE_DIR
		if [ ! -d "$DATABASE_DIR/data/" ]; then
			echo "No directory $DATABASE_DIR/data/"
			echo "Wrong directory entered."
		else
			need_dir=false;
		fi
	done
}

recover_snapshot_from_backup() {
	echo "Restoring snapshot from backup"
	(cd "$DATABASE_DIR/data"
		remove_old_data
		clear_commitlog
		tar --same-owner -C "$DATABASE_DIR/data/" -xvf "$RECOVERY_FILE"
		check_for_errors
	)
}

check_for_errors() {
	rc=$?
	if [[ $rc != 0 ]]; then
		exit $rc
	fi
}

repair_keyspaces() {
	if [ "$osname" = "Cygwin" ] ; then
		repair_keyspaces_cygwin
	else
		repair_keyspaces_linux
	fi
}

fail() {
	echo "Database recovery failed, please check console output for more details"
}

finish() {
	echo "Finished snapshot recovery"
}

{
	init && stop_cassandra && recover_snapshot_from_backup && restore_data && remove_recovery && start_cassandra && repair_keyspaces && finish
} || {
	fail
}