#!/usr/bin/zsh

zmodload zsh/datetime
zmodload -F zsh/stat b:zstat

readonly WEATHER_CURL_TIMEOUT="${WEATHER_CURL_TIMEOUT:-5}"

function weather_check_requirements() {
	[ -r "$(_weather_api_key_path)" ] && \
	[ -r "$(_weather_home_location_key_path)" ] && \
	command -v jq &> /dev/null && \
	command -v curl &> /dev/null && \
	command -v mktemp &> /dev/null
}

function weather_short_status() {
	(
		set -euo pipefail

		API_KEY_PATH="$(_weather_api_key_path)"
		if ! [ -r "${API_KEY_PATH}" ]; then
			read -r 'RESPONSE?Accuweather API key: '
			echo "Storing key at ${API_KEY_PATH}"
			echo "${RESPONSE}" > "${API_KEY_PATH}"
		fi

		WEATHER_API_KEY="$(< "${API_KEY_PATH}")"

		HOME_LOCATION_KEY_PATH="$(_weather_home_location_key_path)"
		if ! [ -r "${HOME_LOCATION_KEY_PATH}" ]; then
			read -r 'RESPONSE?Accuweather location key for your home: '
			echo "Storing home location key at ${HOME_LOCATION_KEY_PATH}"
			echo "${RESPONSE}" > "${HOME_LOCATION_KEY_PATH}"
		fi

		WEATHER_LOCATION_KEY="$(< "${HOME_LOCATION_KEY_PATH}")"

		_weather_current_conditions | jq -er "$(<<'EOF'
first |
[
	"RF:"+(.RealFeelTemperature.Imperial.Value | tostring)+"°",
	"RFS:"+(.RealFeelTemperatureShade.Imperial.Value | tostring)+"°",
	"A:"+(.Temperature.Imperial.Value | tostring)+"°",
	(.RelativeHumidity | tostring)+"%",
	(.Wind.Speed.Imperial.Value | tostring)+"mph",
	.WeatherText,
	.PrecipitationType
] |
join(" ") |
rtrimstr(" ")
EOF
		)" 2>/dev/null
	)
}

function _weather_cache_dir() {
	local -r CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/weather"
	mkdir -p "${CACHE_DIR}" || return $?

	echo "${CACHE_DIR}"
}

function _weather_config_dir() {
	local -r CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/weather"
	mkdir -p "${CONFIG_DIR}" || return $?

	echo "${CONFIG_DIR}"
}

function _weather_api_key_path() {
	(
		set -euo pipefail
		echo "$(_weather_config_dir)/api_key.txt"
	)
}

function _weather_home_location_key_path() {
	(
		set -euo pipefail
		echo "$(_weather_config_dir)/home_location_key.txt"
	)
}

function _weather_api_key() {
	(
		set -euo pipefail
		> /dev/stdout < "$(_weather_api_key_path)"
	)
}

function _weather_cache_dir_location() {
	if [ "${WEATHER_LOCATION_KEY:-}" = "" ]; then
		>&2 echo "Must supply WEATHER_LOCATION_KEY"
		return 1
	fi

	local CACHE_DIR
	CACHE_DIR="$(_weather_cache_dir)/${WEATHER_LOCATION_KEY}"

	mkdir -p "${CACHE_DIR}" || return $?

	echo "${CACHE_DIR}"
}

function _weather_file_ctime() {
	if [ -z "${1:-}" ]; then
		>&2 echo 'Must supply a file path'
		return 1
	fi

	local FILE_STAT

	zstat -H FILE_STAT "${1}" || return $?

	echo "${FILE_STAT[ctime]}"
}

function _weather_file_size() {
	if [ -z "${1:-}" ]; then
		>&2 echo 'Must supply a file path'
		return 1
	fi

	local FILE_STAT

	zstat -H FILE_STAT "${1}" || return $?

	echo "${FILE_STAT[size]}"
}

function _weather_current_conditions() {
	(
		set -euo pipefail

		CURRENT_WEATHER_CACHE_FILE="$(_weather_cache_dir_location)/current_conditions.json"

		if [ -r "${CURRENT_WEATHER_CACHE_FILE}" ]; then
			CTIME="$(_weather_file_ctime "${CURRENT_WEATHER_CACHE_FILE}")"
			SIZE="$(_weather_file_size "${CURRENT_WEATHER_CACHE_FILE}")"
		else
			CTIME=0
			SIZE=0
		fi

		# has it been less than 20 minutes since the last update?
		if [ $((EPOCHSECONDS-CTIME)) -lt 1200 ] && [ "${SIZE}" != "0" ]; then
			> /dev/stdout < "${CURRENT_WEATHER_CACHE_FILE}"
			return
		fi

		TEMP_OUTPUT="$(mktemp)"
		trap 'rm -f "${TEMP_OUTPUT}"' EXIT

		(
			set +e
			curl -LSsf \
				--max-time "${WEATHER_CURL_TIMEOUT}" \
				--get \
				-X GET \
				--data-urlencode "apikey=${WEATHER_API_KEY}" \
				--data-urlencode "language=en-us" \
				--data-urlencode "details=true" \
				"https://dataservice.accuweather.com/currentconditions/v1/${WEATHER_LOCATION_KEY}" \
				-o "${TEMP_OUTPUT}"
			if [ $? -eq 28 ]; then
				# timeout reached, just store empty data
				echo '{}' > "${TEMP_OUTPUT}"
			fi
		)

		mv -f "${TEMP_OUTPUT}" "${CURRENT_WEATHER_CACHE_FILE}"
		> /dev/stdout < "${CURRENT_WEATHER_CACHE_FILE}"
	)
}

function _weather_12_hour_forecast() {
	(
		set -euo pipefail

		TWELVE_HOUR_FORECAST_CACHE_FILE="$(_weather_cache_dir_location)/12_hour_forecast.json"

		if [ -r "${TWELVE_HOUR_FORECAST_CACHE_FILE}" ]; then
			CTIME="$(_weather_file_ctime "${TWELVE_HOUR_FORECAST_CACHE_FILE}")"
			SIZE="$(_weather_file_size "${TWELVE_HOUR_FORECAST_CACHE_FILE}")"
		else
			CTIME=0
			SIZE=0
		fi

		# has it been less than 3 hours since the last update?
		if [ $((EPOCHSECONDS-CTIME)) -lt 10800 ] && [ "${SIZE}" != "0" ]; then
			> /dev/stdout < "${TWELVE_HOUR_FORECAST_CACHE_FILE}"
			return
		fi

		TEMP_OUTPUT="$(mktemp)"
		trap 'rm -f "${TEMP_OUTPUT}"' EXIT

		curl -LSsf \
			--max-time "${WEATHER_CURL_TIMEOUT}" \
			--get \
			-X GET \
			--data-urlencode "apikey=${WEATHER_API_KEY}" \
			--data-urlencode "language=en-us" \
			--data-urlencode "details=true" \
			"https://dataservice.accuweather.com/forecasts/v1/hourly/12hour/${WEATHER_LOCATION_KEY}" \
			-o "${TEMP_OUTPUT}"

		mv -f "${TEMP_OUTPUT}" "${TWELVE_HOUR_FORECAST_CACHE_FILE}"
		> /dev/stdout < "${TWELVE_HOUR_FORECAST_CACHE_FILE}"
	)
}

function _weather_5_day_daily_forecast() {
	(
		set -euo pipefail

		FIVE_DAY_DAILY_FORECAST_CACHE_FILE="$(_weather_cache_dir_location)/5_day_daily_forecast.json"

		if [ -r "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}" ]; then
			CTIME="$(_weather_file_ctime "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}")"
			SIZE="$(_weather_file_size "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}")"
		else
			CTIME=0
			SIZE=0
		fi

		# has it been less than 12 hours since the last update?
		if [ $((EPOCHSECONDS-CTIME)) -lt 43200 ] && [ "${SIZE}" != "0" ]; then
			> /dev/stdout < "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}"
			return
		fi

		TEMP_OUTPUT="$(mktemp)"
		trap 'rm -f "${TEMP_OUTPUT}"' EXIT

		curl -LSsf \
			--max-time "${WEATHER_CURL_TIMEOUT}" \
			--get \
			-X GET \
			--data-urlencode "apikey=${WEATHER_API_KEY}" \
			--data-urlencode "language=en-us" \
			--data-urlencode "details=true" \
			"https://dataservice.accuweather.com/forecasts/v1/daily/5day/${WEATHER_LOCATION_KEY}" \
			-o "${TEMP_OUTPUT}"

		mv -f "${TEMP_OUTPUT}" "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}"
		> /dev/stdout < "${FIVE_DAY_DAILY_FORECAST_CACHE_FILE}"
	)
}