#!/usr/bin/env bash

# ----------
# Default Configuration
# ----------
webhook_url=""       # Incoming Webhooks integration URL
upload_token=""      # The user's API authentication token, only used for file uploads
channel="general"    # Default channel to post messages. '#' is prepended, if it doesn't start with '#' or '@'.
tmp_dir="/tmp"       # Temporary file is created in this directory.
username="slacktee"  # Default username to post messages.
icon="ghost"         # Default emoji to post messages. Don't wrap it with ':'. See http://www.emoji-cheat-sheet.com; can be a url too.
attachment=""        # Default color of the attachments. If an empty string is specified, the attachments are not used.

# ----------
# Initialization
# ----------
me=$(basename "$0")
title=""
mode="buffering"
link=""
textWrapper="\`\`\`"
parseMode=""
fields=()
# Since bash 3 doesn't support the associative array, we store colors and patterns separately
cond_color_colors=()
cond_color_patterns=()
found_pattern_color=""
# This color is used when 'attachment' is used without color specification
internal_default_color="#C0C0C0"

# Since bash 3 doesn't support the associative array, we store prefixes and patterns separately
cond_prefix_prefixes=()
cond_prefix_patterns=()
found_title_prefix=""

function show_help()
{
	echo "usage: $me [options]"
	echo "  options:"
	echo "    -h, --help                        Show this help."
	echo "    -n, --no-buffering                Post input values without buffering."
	echo "    -f, --file                        Post input values as a file."
	echo "    -l, --link                        Add a URL link to the message."
	echo "    -c, --channel channel_name        Post input values to specified channel or user."
	echo "    -u, --username user_name          This username is used for posting."
	echo "    -i, --icon emoji_name|url         This icon is used for posting. You can use a word"
	echo "                                      from http://www.emoji-cheat-sheet.com or a direct url to an image."
	echo "    -t, --title title_string          This title is added to posts."
	echo "    -m, --message-formatting format   Switch message formatting (none|link_names|full)."
	echo "                                      See https://api.slack.com/docs/formatting for more details."
	echo "    -p, --plain-text                  Don't surround the post with triple backticks."
	echo "    -a, --attachment [color]          Use attachment (richly-formatted message)"
	echo "                                      Color can be 'good','warning','danger' or any hex color code (eg. #439FE0)"
	echo "                                      See https://api.slack.com/docs/attachments for more details."
	echo "    -e, --field title value           Add a field to the attachment. You can specify this multiple times."
	echo "    -s, --short-field title value     Add a short field to the attachment. You can specify this multiple times."
	echo "    -o, --cond-color color pattern    Change the attachment color if the specified Regex pattern matches the input."
	echo "                                      You can specify this multile times."
	echo "                                      If more than one pattern matches, the latest matched pattern is used."
	echo "    -d, --cond-prefix prefix pattern  This prefix is added to the message, if the specified Regex pattern matches the input."
	echo "                                      You can specify this multile times."
	echo "                                      If more than one pattern matches, the latest matched pattern is used."
	echo "    --config config_file              Specify the location of the config file."
	echo "    --setup                           Setup slacktee interactively."
}



function send_message()
{
	message="$1"

	# Prepend the prefix to the message, if it's set
	if [[ -z $attachment && -n $found_pattern_prefix ]]; then
		message="$found_pattern_prefix$message"
		# Clear conditional prefix for the nest send
		found_pattern_prefix=""
	fi

	escaped_message=$(echo "$textWrapper\n$message\n$textWrapper" | sed 's/"/\\"/g' | sed "s/'/\\'/g" )
	message_attr=""
	if [[ $message != "" ]]; then
		if [[ -n $attachment ]]; then

			# Set message color
			message_color="$attachment"
			if [[ -n $found_pattern_color ]]; then
				message_color="$found_pattern_color"
				# Reset with the default color for the next send
				found_pattern_color="$attachment"
			fi

			message_attr="\"attachments\": [{ \"color\": \"$message_color\", \"mrkdwn_in\": [\"text\", \"fields\"], \"text\": \"$escaped_message\" "

			if [[ -n $found_pattern_prefix ]]; then
				orig_title=$title
				title="$found_pattern_prefix $title"
				# Clear conditional prefix for the nest send
				found_pattern_prefix=""
			fi

			if [[ -n $title ]]; then
				message_attr="$message_attr, \"title\": \"$title\" "
				# Clear conditional prefix from title
				title=$orig_title
			fi

			if [[ -n $link ]]; then
				message_attr="$message_attr, \"title_link\": \"$link\" "
			fi

			if [[ $mode == "file" ]]; then
				fields+=("{\"title\": \"Access URL\", \"value\": \"$access_url\" }")
				fields+=("{\"title\": \"Download URL\", \"value\": \"$download_url\"}")
			fi

			if [[ ${#fields[@]} != 0 ]]; then
				message_attr="$message_attr, \"fields\": ["
				for field in "${fields[@]}"; do
					message_attr="$message_attr $field,"
				done
				message_attr=${message_attr%?} # Remove last comma
				message_attr="$message_attr ]"
			fi

			# Close attachment
			message_attr="$message_attr }], "
		else
			message_attr="\"text\": \"$escaped_message\","
		fi

		icon_url="$icon"
		#icon_emoji=""
		#if echo "$icon" | grep -q "^https\?://.*"; then
		#	icon_url="$icon"
		#else
	 #	icon_emoji=":$icon:"
		#fi

		json="{\"channel\": \"$channel\", \"username\": \"$username\", $message_attr \"icon_emoji\": \"$icon_emoji\", \"icon_url\": \"$icon_url\" $parseMode}"
		post_result=$(curl -X POST --data-urlencode "payload=$json" "$webhook_url" 2> /dev/null)
		exit_code=1
		if [[ $post_result == "ok" ]]; then
			exit_code=0
		fi
	fi
}

function process_line()
{
	echo "$1"
	line="$(echo "$1" | sed $'s/\t/  /g')"

	# Check the patterns of the conditional colors
	# If more than one pattern matches, the latest pattern is used
	if [[ ${#cond_color_patterns[@]} != 0 ]]; then
		for i in "${!cond_color_patterns[@]}"; do
			if [[ $line =~ ${cond_color_patterns[$i]} ]]; then
				found_pattern_color=${cond_color_colors[$i]}
			fi
		done
	fi

	# Check the patterns of the conditional titles
	# If more than one pattern matches, the latest pattern is used
	if [[ ${#cond_prefix_patterns[@]} != 0 ]]; then
		for i in "${!cond_prefix_patterns[@]}"; do
			if [[ $line =~ ${cond_prefix_patterns[$i]} ]]; then
				found_pattern_prefix=${cond_prefix_prefixes[$i]}
				if [[ -n $attachment || $mode != "no-buffering" ]]; then
					# Append a line break to the prefix for better formatting
					found_pattern_prefix="$found_pattern_prefix\n"
				else
					# Append a space to the prefix for better formatting
					found_pattern_prefix="$found_pattern_prefix "
				fi
			fi
		done
	fi

	if [[ $mode == "no-buffering" ]]; then
		prefix=''
		if [[ -z $attachment ]]; then
			prefix=$title
		fi
		send_message "$prefix$line"
	elif [[ $mode == "file" ]]; then
		echo "$line" >> "$filename"
	else
		if [[ -z "$text" ]]; then
			text="$line"
		else
			text="$text\n$line"
		fi
	fi
}

function setup()
{
	if [[ -z "$HOME" ]]; then
		echo "\$HOME is not defined. Please set it first."
		exit 1
	fi

	local_conf="$HOME/.slacktee"

	if [[ -e "$local_conf" ]]; then
		echo ".slacktee is found in your home directory."
		read -p "Are you sure to overwrite it? [y/n] :" choice
		case "$choice" in
			y|Y )
				# Continue
				;;
			* )
				exit 0 # Abort
				;;
		esac
	fi

	# Load current local config
	. $local_conf

	# Start setup
	read -p "Incoming Webhook URL [$webhook_url]: " input_webhook_url
	if [[ -z "$input_webhook_url" ]]; then
		input_webhook_url=$webhook_url
	fi
	read -p "Upload Token [$upload_token]: " input_upload_token
	if [[ -z "$input_upload_token" ]]; then
		input_upload_token=$upload_token
	fi
	read -p "Temporary Directory [$tmp_dir]: " input_tmp_dir
	if [[ -z "$input_tmp_dir" ]]; then
		input_tmp_dir=$tmp_dir
	fi
	read -p "Default Channel [$channel]: " input_channel
	if [[ -z "$input_channel" ]]; then
		input_channel=$channel
	fi
	read -p "Default Username [$username]: " input_username
	if [[ -z "$input_username" ]]; then
		input_username=$username
	fi
	read -p "Default Icon: [$icon]: " input_icon
	if [[ -z "$input_icon" ]]; then
		input_icon=$icon
	fi
	read -p "Default color of the attachment. (empty string disables attachment) [$attachment]: " input_attachment
	if [[ -z "$input_attachment" ]]; then
		input_attachment=$attachment
	elif [[ $input_attachment == '""' || $input_attachment == "''" ]]; then
		input_attachment=""
	fi

	cat <<- EOF | sed 's/^[[:space:]]*//' > "$local_conf"
	webhook_url="$input_webhook_url"
	upload_token="$input_upload_token"
	tmp_dir="$input_tmp_dir"
	channel="$input_channel"
	username="$input_username"
	icon="$input_icon"
	attachment="$input_attachment"
	EOF
}

# ----------
# Parse command line options
# ----------
OPTIND=1

while [[ $# -gt 0 ]]; do
	opt="$1"
	shift

	case "$opt" in
		-h|\?|--help)
			show_help
			exit 0
			;;
		-n|--no-buffering)
			mode="no-buffering"
			;;
		-f|--file)
			mode="file"
			;;
		-l|--link)
			link="$1"
			shift
			;;
		-c|--channel)
			opt_channel="$1"
			shift
			;;
		-u|--username)
			opt_username="$1"
			shift
			;;
		-i|--icon)
			opt_icon="$1"
			shift
			;;
		-t|--title)
			title="$1"
			shift
			;;
		-d|--cond-prefix)
			case "$1" in
				-*|'')
					# Found next command line option or empty. Error.
					echo "a prefix of the conditional title was not specified"
					show_help
					exit 1
					;;
				*)
					# Prefix should be found
					case "$2" in
						-*|'')
							# Found next command line option or empty. Error.
							echo "a pattern of the conditional title was not specified"
							show_help
							exit 1
							;;
						*)
							# Set the prefix and the pattern to arrays
							cond_prefix_prefixes+=("$1")
							cond_prefix_patterns+=("$2")
							shift
							shift
							;;
					esac
					;;
			esac
			;;
		-m|--message-formatting)
			case "$1" in
				none)
					parseMode=', "parse": "none"'
					;;
				link_names)
					parseMode=', "link_names": "1"'
					;;
				full)
					parseMode=', "parse": "full"'
					;;
				*)
					echo "unknown message formatting option"
					show_help
					exit 1
					;;
			esac
			shift
			;;
		-p|--plain-text)
			textWrapper=""
			;;

		-a|--attachment)
			case "$1" in
				-*|'')
					# Found next command line option
					opt_attachment="$internal_default_color" # Use default color
					;;
				\#*|good|warning|danger)
					# Found hex color code or predefined colors
					opt_attachment="$1"
					shift
					;;
				*)
					echo "unknown attachment color"
					show_help
					exit 1
					;;
			esac
			;;
		-o|--cond-color)
			case "$1" in
				-*|'')
					# Found next command line option or empty. Error.
					echo "a color of the conditional color was not specified"
					show_help
					exit 1
					;;
				\#*|good|warning|danger)
					# Found hex color code or predefined colors
					case "$2" in
						-*|'')
							# Found next command line option or empty. Error.
							echo "a pattern of the conditional color was not specified"
							show_help
							exit 1
							;;
						*)
							# Set the color and the pattern to arrays
							cond_color_colors+=("$1")
							cond_color_patterns+=("$2")
							shift
							shift
							;;
					esac
					;;
				*)
					echo "unknown attachment color $1"
					show_help
					exit 1
				;;
			esac
			;;
		-e|-s|--field|--short-field)
			case "$1" in
				-*|'')
					# Found next command line option or empty. Error.
					echo "field title was not specified"
					show_help
					exit 1
					;;
				*)
					case "$2" in
						-*|'')
							# Found next command line option or empty. Error.
							echo "field value was not specified"
							show_help
							exit 1
							;;
						*)
							if [[ $opt == "-s" || $opt == "--short-field" ]]; then
								fields+=("{\"title\": \"$1\", \"value\": \"$2\", \"short\": true}")
							else
								fields+=("{\"title\": \"$1\", \"value\": \"$2\"}")
							fi
							shift
							shift
							;;
					esac
			esac
			;;
		--config)
			CUSTOM_CONFIG=$1
			shift
			;;
		--setup)
			setup
			exit 1
			;;
		*)
			echo "illegal option $opt"
			show_help
			exit 1
			;;
	esac
done

# ---------
# Read in our configurations
# ---------
if [[ -e "/etc/slacktee.conf" ]]; then
	. /etc/slacktee.conf
fi

if [[ -n "$HOME" && -e "$HOME/.slacktee" ]]; then
	. "$HOME/.slacktee"
fi

if [[ -e "$CUSTOM_CONFIG" ]]; then
	. $CUSTOM_CONFIG
fi

# Overwrite webhook_url if the environment variable SLACKTEE_WEBHOOK is set
if [[ "$SLACKTEE_WEBHOOK" != "" ]]; then
	webhook_url="$SLACKTEE_WEBHOOK"
fi

# Overwrite upload_token if the environment variable SLACKTEE_TOKEN is set
if [[ "$SLACKTEE_TOKEN" != "" ]]; then
	upload_token="$SLACKTEE_TOKEN"
fi

# Overwrite channel if it's specified in the command line option
if [[ "$opt_channel" != "" ]]; then
	channel="$opt_channel"
fi

# Overwrite username if it's specified in the command line option
if [[ "$opt_username" != "" ]]; then
	username="$opt_username"
fi

# Overwrite icon if it's specified in the command line option
if [[ "$opt_icon" != "" ]]; then
	icon="$opt_icon"
fi

# Overwrite attachment if it's specified in the command line option
if [[ "$opt_attachment" != "" ]]; then
	attachment="$opt_attachment"
fi

# Set the default color to attachment if it's still empty and the length of the cond_color_patterns is not 0
if [[ -z $attachment ]] && [[ ${#cond_color_patterns[@]} != 0 ]]; then
	attachment="$internal_default_color"
fi

# ----------
# Validate configurations
# ----------

if [[ $webhook_url == "" ]]; then
	echo "Please setup the webhook url of this incoming webhook integration."
	exit 1
fi

if [[ $upload_token == "" && $mode == "file" ]]; then
	echo "Please provide the authentication token for file uploads."
	exit 1
fi

if [[ $channel == "" ]]; then
	echo "Please specify a channel."
	exit 1
elif [[ ( "$channel" != "#"* ) && ( "$channel" != "@"* ) ]]; then
	channel="#$channel"
fi

if [[ -n "$icon" ]]; then
	icon=${icon#:} # remove leading ':'
	icon=${icon%:} # remove trailing ':'
fi

# ----------
# Start script
# ----------

text=""
if [[ -n "$title" || -n "$link" ]]; then
	# Use link as title, if title is not specified
	if [[ -z "$title" ]]; then
		title="$link"
	fi

	# Add title to filename in the file mode
	if [[ "$mode" == "file" ]]; then
		filetitle=$(echo "$title"|sed 's/[ /:.]//g')
		filetitle="$filetitle-"
	fi

	if [[ -z "$attachment" ]]; then
		if [[ "$mode" == "no-buffering" ]]; then
			if [[ -n "$link" ]]; then
				title="<$link|$title>: "
			else
				title="$title: "
			fi
		elif [[ "$mode" == "file" ]]; then
			if [[ -n "$link" ]]; then
				title="<$link|$title>"
			fi
		else
			if [[ -n "$link" ]]; then
				text="-- <$link|$title> --\n"
			else
				text="-- $title --\n"
			fi
		fi
	fi
fi

timestamp="$(date +'%m%d%Y-%H%M%S')"
filename="$tmp_dir/$filetitle$$-$timestamp.log"

if [[ "$mode" == "file" ]]; then
	touch $filename
fi

exit_code=0

while IFS='' read line; do
	process_line "$line"
done
if [[ -n $line ]]; then
	process_line "$line"
fi

if [[ "$mode" == "buffering" ]]; then
	send_message "$text"
elif [[ "$mode" == "file" ]]; then
	if [[ -s "$filename" ]]; then
		channels_param=""
		if [[ ( "$channel" == "#"* ) ]]; then
			# Set channels for making the file public
			channels_param="-F channels=$channel"
		fi
		result="$(curl -F file=@"$filename" -F token="$upload_token" $channels_param https://slack.com/api/files.upload 2> /dev/null)"
		access_url="$(echo "$result" | awk 'match($0, /url_private":"([^"]*)"/) {print substr($0, RSTART+14, RLENGTH-15)}'|sed 's/\\//g')"
		download_url="$(echo "$result" | awk 'match($0, /url_private_download":"([^"]*)"/) {print substr($0, RSTART+23, RLENGTH-24)}'|sed 's/\\//g')"
		if [[ -n "$attachment" ]]; then
			text="Input file has been uploaded"
		else
			if [[ "$title" != "" ]]; then
				title=" of $title"
			fi
			text="Input file$title has been uploaded.\n$access_url\n\nYou can download it from the link below.\n$download_url"
		fi
		send_message "$text"
	fi
	# Clean up the temp file
	rm "$filename"
fi

exit $exit_code