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

usage() {
  cat <<'EOF'
Usage:
  HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
    scripts/run-hypertwist-remote-unreal-build.sh

Options:
  --target <name>
      Optional Unreal build target. Defaults to UnrealHyperTwistEditor.

  --platform <name>
      Optional Unreal platform. Defaults to Win64.

  --configuration <name>
      Optional Unreal configuration. Defaults to Development.

  --worktree-root <path>
      Optional Windows worktree root. Defaults to C:\HyperTwist_worktrees\phase10validate.

  --project-path <path>
      Optional Windows .uproject path. Defaults beneath the worktree root.

  --build-batch-path <path>
      Optional Build.bat path. Defaults to the UE 5.7 installed-engine path.

  --max-parallel-actions <count>
      Optional MaxParallelActions override. Defaults to 2 for the remote lane.

  --allow-uba
      Allow UBA instead of forcing the verified remote-lane -NoUBA posture.

  --source-lock-retries <count>
      Retry the remote build when UnrealBuildTool hits a transient Windows
      source-file lock. Defaults to 1.

  --source-lock-retry-delay-seconds <count>
      Delay between transient source-lock retries. Defaults to 3 seconds.

  --extra-arg <value>
      Append an extra Build.bat argument. May be repeated.

  --dry-run
      Print the generated remote PowerShell payload instead of executing it.

Environment:
  HYPERTWIST_REMOTE_WINDOWS_PASSWORD      Required by the underlying tunnel wrapper.
  HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT Optional default for --worktree-root.
  HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH  Optional default for --project-path.
  HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT     Optional default for --build-batch-path.
  HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS
                                         Optional default for --max-parallel-actions.
  HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRIES
                                         Optional default for --source-lock-retries.
  HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRY_DELAY_SECONDS
                                         Optional default for --source-lock-retry-delay-seconds.
EOF
}

target_name="UnrealHyperTwistEditor"
platform_name="Win64"
configuration_name="Development"
worktree_root="${HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT:-C:\\HyperTwist_worktrees\\phase10validate}"
project_path=""
build_batch_path="${HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT:-C:\\Program Files\\Epic Games\\UE_5.7\\Engine\\Build\\BatchFiles\\Build.bat}"
max_parallel_actions="${HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS:-2}"
source_lock_retries="${HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRIES:-1}"
source_lock_retry_delay_seconds="${HYPERTWIST_REMOTE_WINDOWS_SOURCE_LOCK_RETRY_DELAY_SECONDS:-3}"
allow_uba="false"
dry_run="false"
extra_args=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    --target)
      target_name="${2:-}"
      shift 2
      ;;
    --platform)
      platform_name="${2:-}"
      shift 2
      ;;
    --configuration)
      configuration_name="${2:-}"
      shift 2
      ;;
    --worktree-root)
      worktree_root="${2:-}"
      shift 2
      ;;
    --project-path)
      project_path="${2:-}"
      shift 2
      ;;
    --build-batch-path)
      build_batch_path="${2:-}"
      shift 2
      ;;
    --max-parallel-actions)
      max_parallel_actions="${2:-}"
      shift 2
      ;;
    --allow-uba)
      allow_uba="true"
      shift
      ;;
    --source-lock-retries)
      source_lock_retries="${2:-}"
      shift 2
      ;;
    --source-lock-retry-delay-seconds)
      source_lock_retry_delay_seconds="${2:-}"
      shift 2
      ;;
    --extra-arg)
      extra_args+=("${2:-}")
      shift 2
      ;;
    --dry-run)
      dry_run="true"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      usage >&2
      exit 1
      ;;
  esac
done

if [[ -z "$project_path" ]]; then
  project_path="${HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH:-${worktree_root}\\UnrealHyperTwist\\UnrealHyperTwist.uproject}"
fi

if [[ -z "$target_name" || -z "$platform_name" || -z "$configuration_name" || -z "$project_path" || -z "$build_batch_path" ]]; then
  echo "Target, platform, configuration, project path, and Build.bat path must all be populated." >&2
  exit 1
fi

if [[ -n "$max_parallel_actions" && ! "$max_parallel_actions" =~ ^[0-9]+$ ]]; then
  echo "--max-parallel-actions must be numeric." >&2
  exit 1
fi

if [[ -n "$source_lock_retries" && ! "$source_lock_retries" =~ ^[0-9]+$ ]]; then
  echo "--source-lock-retries must be numeric." >&2
  exit 1
fi

if [[ -n "$source_lock_retry_delay_seconds" && ! "$source_lock_retry_delay_seconds" =~ ^[0-9]+$ ]]; then
  echo "--source-lock-retry-delay-seconds must be numeric." >&2
  exit 1
fi

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
remote_ps_wrapper="${script_dir}/run-hypertwist-remote-windows-powershell.sh"

if [[ ! -x "$remote_ps_wrapper" ]]; then
  echo "Expected executable wrapper at $remote_ps_wrapper" >&2
  exit 1
fi

powershell_payload="$(
  python3 - <<'PY' \
    "$build_batch_path" \
    "$target_name" \
    "$platform_name" \
    "$configuration_name" \
    "$project_path" \
    "$max_parallel_actions" \
    "$allow_uba" \
    "${extra_args[@]}"
import sys

build_batch_path, target_name, platform_name, configuration_name, project_path, max_parallel_actions, allow_uba, *extra_args = sys.argv[1:]

def ps_single_quote(value: str) -> str:
    return "'" + value.replace("'", "''") + "'"

lines = [
    f"$BuildBatchPath = {ps_single_quote(build_batch_path)}",
    f"$TargetName = {ps_single_quote(target_name)}",
    f"$PlatformName = {ps_single_quote(platform_name)}",
    f"$ConfigurationName = {ps_single_quote(configuration_name)}",
    f"$ProjectPath = {ps_single_quote(project_path)}",
    f"$MaxParallelActions = {max_parallel_actions}",
    f"$AllowUba = ${'true' if allow_uba == 'true' else 'false'}",
    "$ExtraArgs = @(",
]

for value in extra_args:
    lines.append(f"  {ps_single_quote(value)}")

lines.extend([
    ")",
    "",
    "if (-not (Test-Path -LiteralPath $BuildBatchPath)) {",
    "  throw \"Build.bat was not found at '$BuildBatchPath'.\"",
    "}",
    "",
    "if (-not (Test-Path -LiteralPath $ProjectPath)) {",
    "  throw \"Project file was not found at '$ProjectPath'.\"",
    "}",
    "",
    "$BuildArgs = @(",
    "  $TargetName",
    "  $PlatformName",
    "  $ConfigurationName",
    "  $ProjectPath",
    "  '-WaitMutex'",
    "  '-NoHotReloadFromIDE'",
    ")",
    "",
    "if (-not $AllowUba) {",
    "  $BuildArgs += '-NoUBA'",
    "}",
    "",
    "if ($MaxParallelActions -gt 0) {",
    "  $BuildArgs += \"-MaxParallelActions=$MaxParallelActions\"",
    "}",
    "",
    "if ($ExtraArgs.Count -gt 0) {",
    "  $BuildArgs += $ExtraArgs",
    "}",
    "",
    "& $BuildBatchPath @BuildArgs",
    "exit $LASTEXITCODE",
])

print("\n".join(lines))
PY
)"

if [[ "$dry_run" == "true" ]]; then
  printf '%s\n' "$powershell_payload"
  exit 0
fi

source_lock_marker="because it is being used by another process"
attempt=1
max_attempts=$((source_lock_retries + 1))
build_output_path="$(mktemp)"
trap 'rm -f "$build_output_path"' EXIT

while :; do
  set +e
  "$remote_ps_wrapper" --command "$powershell_payload" 2>&1 | tee "$build_output_path"
  build_exit="${PIPESTATUS[0]}"
  set -e

  if [[ "$build_exit" -eq 0 ]]; then
    exit 0
  fi

  if ! grep -Fq "$source_lock_marker" "$build_output_path"; then
    exit "$build_exit"
  fi

  if (( attempt >= max_attempts )); then
    echo "Remote Unreal build exhausted ${max_attempts} attempt(s) after transient source-file lock detection." >&2
    exit "$build_exit"
  fi

  attempt=$((attempt + 1))
  echo "Retrying remote Unreal build after transient source-file lock (${attempt}/${max_attempts}) in ${source_lock_retry_delay_seconds}s..." >&2
  sleep "$source_lock_retry_delay_seconds"
done
