#!/usr/bin/env bash
set -uo pipefail

# DO NOT set -e, otherwise error responses won't be shown to the user.

# bs_ios uploads app binaries for UI testing on BrowserStack. Upon successful
# completion, it returns build ID of the test run on BrowserStack.
#
# This script works only with the V2 endpoint (XC Test Plans). This script
# assumes that Xcode Test Plan name is TestPlan.xctestplan.
#
# It forwards all arguments to "patrol build ios", so you can pass --target,
# --flavor, --exclude etc. just as you would pass them to "patrol_cli".
#
# It can also be configured with the following environment variables:
BS_CREDENTIALS="${BS_CREDENTIALS:-}"
BS_PROJECT="${BS_PROJECT:-}"
BS_IOS_DEVICES="${BS_IOS_DEVICES:-}"
BS_SKIP_BUILD="${BS_SKIP_BUILD:-0}"
BS_IDLE_TIMEOUT="${BS_IDLE_TIMEOUT:-900}"

if ! command -v jq >/dev/null 2>&1; then
	echo 1>&2 "Error: jq not found"
	exit 1
fi

# Capture all arguments because they'll be consumed by getopt
original_args=("$@")

# Arg parsing start
FLAVOR=""
FLAVOR_PREFIXED="Runner"
FLAVOR_SUFFIXED="-"

TEMP=$(getopt -n "$0" -a -l "flavor:" -- -- "$@")
eval set -- "$TEMP"
while [ $# -gt 0 ]; do
	case "$1" in
	--flavor)
		FLAVOR="$2"
		shift
		;;
	--) shift ;;
	esac
	shift
done

if [ -n "$FLAVOR" ]; then
	echo 1>&2 "Passed flavor: $FLAVOR"
	FLAVOR_SUFFIXED="-$FLAVOR-"
	FLAVOR_PREFIXED="$FLAVOR"
fi
# Arg parsing end

if [ -z "${BS_CREDENTIALS:-}" ]; then
	echo 1>&2 "Error: BS_CREDENTIALS not set"
	exit 1
fi

if [ -z "$BS_PROJECT" ]; then
	default_project="Unnamed iOS project"
	echo 1>&2 "BS_PROJECT not set, using default: $default_project"
	BS_PROJECT="$default_project"
fi

if [ -z "$BS_IOS_DEVICES" ]; then
	default_devices="[\"iPhone 14-16\"]"
	echo 1>&2 "BS_IOS_DEVICES not set, using default: $default_devices"
	BS_IOS_DEVICES="$default_devices"
fi

if [ "$BS_SKIP_BUILD" = 1 ]; then
	echo 1>&2 "BS_SKIP_BUILD set to 1, build was skipped"
else
	patrol 1>&2 build ios --release "${original_args[@]}"
fi

echo 1>&2 "Will create zip archive of test files"

cd build/ios_integ/Build/Products || exit 1

rm -rf Payload && mkdir -p Payload
cp -r "Release${FLAVOR_SUFFIXED}iphoneos/Runner.app" Payload
zip -r Runner.ipa Payload >/dev/null

cd - >/dev/null || exit 1

cd "build/ios_integ/Build/Products/Release${FLAVOR_SUFFIXED}iphoneos" || exit 1
rm -rf ios_tests.zip

# BrowserStack fails if DiagnosticCollectionPolicy is present
plutil -remove 'TestConfigurations.TestTargets.DiagnosticCollectionPolicy' ../"${FLAVOR_PREFIXED}"_TestPlan_iphoneos*.xctestrun

cp ../"${FLAVOR_PREFIXED}"_TestPlan_iphoneos*.xctestrun .
zip -r ios_tests.zip "${FLAVOR_PREFIXED}"_TestPlan_iphoneos*.xctestrun RunnerUITests-Runner.app >/dev/null
cd - >/dev/null || exit 1

echo 1>&2 "Created zip archive"

app_path="$PWD/build/ios_integ/Build/Products/Runner.ipa"
if [ ! -f "$app_path" ]; then
	echo 1>&2 "Error: app under test not found at $app_path"
	exit 1
fi

# https://www.browserstack.com/docs/app-automate/api-reference/xcuitest/apps#upload-an-app
printf 1>&2 "Will upload app under test from %s\n\n" "$app_path"
if ! app_upload_response="$(
	curl --fail-with-body --user "$BS_CREDENTIALS" \
		--request POST "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/app" \
		--form "file=@$app_path"
)"; then
	echo 1>&2 "Error: failed to upload app under test"
	echo 1>&2 "$app_upload_response"
	exit 1
fi

app_url="$(echo "$app_upload_response" | jq --raw-output .app_url)"
printf 1>&2 "\nUploaded app under test, url: %s\n" "$app_url"

test_path="$PWD/build/ios_integ/Build/Products/Release${FLAVOR_SUFFIXED}iphoneos/ios_tests.zip"
if [ ! -f "$test_path" ]; then
	echo 1>&2 "Error: zip archive of test suite not found at $test_path"
	exit 1
fi

# https://www.browserstack.com/docs/app-automate/api-reference/xcuitest/tests#upload-a-test-suite
printf 1>&2 "Will upload zip archive of test suite from %s\n\n" "$test_path"
if ! test_upload_response="$(
	curl --fail-with-body --user "$BS_CREDENTIALS" \
		--request POST "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/test-suite" \
		--form "file=@$test_path"
)"; then
	echo 1>&2 "Error: failed to upload zip archive of test suite"
	echo 1>&2 "$test_upload_response"
	exit 1
fi

test_url="$(echo "$test_upload_response" | jq --raw-output .test_suite_url)"
printf 1>&2 "\nUploaded zip archive of test suite, url: %s\n" "$test_url"

# https://www.browserstack.com/docs/app-automate/api-reference/xcuitest/builds#execute-a-build
printf 1>&2 "Will schedule test execution\n\n"
if ! run_response="$(
	curl --fail-with-body --user "$BS_CREDENTIALS" \
		--request POST "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/xctestrun-build" \
		--header "Content-Type: application/json" \
		--data-binary @- <<EOF
{
    "app": "$app_url",
    "testSuite": "$test_url",
    "project": "$BS_PROJECT",
    "devices": $BS_IOS_DEVICES,
    "deviceLogs": true,
    "enableResultBundle": true,
    "idleTimeout": $BS_IDLE_TIMEOUT
}
EOF
)"; then
	echo 1>&2 "Error: failed to schedule test execution"
	echo 1>&2 "$run_response"
	exit 1
fi

printf 1>&2 "\n\nScheduled test execution\n"

build_id="$(echo "$run_response" | jq --raw-output .build_id)"
echo "$build_id"
