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

###############################################################################
# run-unity-docker.sh
#
# Core wrapper that runs any Unity command inside the GameCI Docker container.
# Handles license activation, workspace mounting, and exit code passthrough.
#
# Environment variables:
#   UNITY_VERSION        - Unity Editor version (default: 2021.3.45f1)
#   UNITY_IMAGE_VERSION  - GameCI image version (default: 3)
#   UNITY_LICENSE        - Contents of a machine-matched .ulf license file
#   UNITY_SERIAL         - Pro/manual license serial key
#   UNITY_EMAIL          - Unity account email (required for Pro; recommended for Personal)
#   UNITY_PASSWORD       - Unity account password (required for Pro; recommended for Personal)
#   UNITY_TEST_PROJECT_DIR - Path to test project (default: /home/vscode/.unity-test-project)
#   UNITY_USE_XVFB       - Set to 1 to install xvfb and use xvfb-run (for PlayMode tests)
#   UNITY_TIMEOUT        - Timeout in seconds for Docker run (default: 1800 = 30 min)
#
# Usage:
#   ./run-unity-docker.sh -batchmode -nographics -quit -projectPath /project -logFile -
###############################################################################

UNITY_VERSION="${UNITY_VERSION:-2021.3.45f1}"
UNITY_IMAGE_VERSION="${UNITY_IMAGE_VERSION:-3}"
UNITY_TEST_PROJECT_DIR="${UNITY_TEST_PROJECT_DIR:-/home/vscode/.unity-test-project}"
UNITY_USE_XVFB="${UNITY_USE_XVFB:-0}"
UNITY_TIMEOUT="${UNITY_TIMEOUT:-1800}"

WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Store cache in the persistent test-project volume by default to avoid
# workspace ownership/commit risks. Can be overridden via UNITY_LICENSE_CACHE_DIR.
UNITY_LICENSE_CACHE_DIR="${UNITY_LICENSE_CACHE_DIR:-${UNITY_TEST_PROJECT_DIR}/.unity-license-cache}"
UNITY_LICENSE_CACHE_LOCAL_DIR="${UNITY_LICENSE_CACHE_DIR}/local-share-unity3d"
UNITY_LICENSE_CACHE_CONFIG_DIR="${UNITY_LICENSE_CACHE_DIR}/config-unity3d"

# ── Auto-load credentials from .unity-secrets/ ──────────────────────────────
# File-based secrets take lower priority than environment variables.
# Generated by: pwsh -NoProfile -File scripts/unity/setup-license.ps1
SECRETS_DIR="${WORKSPACE_DIR}/.unity-secrets"

if [[ -z "${UNITY_LICENSE:-}" && -f "${SECRETS_DIR}/license.ulf" ]]; then
    UNITY_LICENSE="$(cat "${SECRETS_DIR}/license.ulf")"
    export UNITY_LICENSE
    echo "==> [run-unity-docker] Loaded license from .unity-secrets/license.ulf"
fi

if [[ -f "${SECRETS_DIR}/credentials.env" ]]; then
    _CREDS_LOADED=0
    # Parse KEY=VALUE pairs safely (no shell execution, skip comments/blanks)
    while IFS='=' read -r key value || [[ -n "${key}" ]]; do
        # Skip comments and empty lines
        [[ -z "${key}" || "${key}" =~ ^[[:space:]]*# ]] && continue
        # Trim whitespace (use printf to avoid echo interpreting -n/-e/backslashes)
        key="$(printf '%s' "${key}" | tr -d '[:space:]')"
        value="$(printf '%s' "${value}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
        # Only set if not already in environment.
        # Note: UNITY_LICENSE_TYPE is intentionally not consumed here — license type
        # is inferred from which env vars are set (UNITY_LICENSE vs UNITY_SERIAL).
        case "${key}" in
            UNITY_SERIAL)
                if [[ -z "${UNITY_SERIAL:-}" ]]; then
                    UNITY_SERIAL="${value}"
                    export UNITY_SERIAL
                    _CREDS_LOADED=1
                fi
                ;;
            UNITY_EMAIL)
                if [[ -z "${UNITY_EMAIL:-}" ]]; then
                    UNITY_EMAIL="${value}"
                    export UNITY_EMAIL
                    _CREDS_LOADED=1
                fi
                ;;
            UNITY_PASSWORD)
                if [[ -z "${UNITY_PASSWORD:-}" ]]; then
                    UNITY_PASSWORD="${value}"
                    export UNITY_PASSWORD
                    _CREDS_LOADED=1
                fi
                ;;
        esac
    done < "${SECRETS_DIR}/credentials.env"
    if [[ "${_CREDS_LOADED}" -eq 1 ]]; then
        echo "==> [run-unity-docker] Loaded credentials from .unity-secrets/credentials.env"
    fi
    unset _CREDS_LOADED
fi

UNITY_IMAGE="unityci/editor:ubuntu-${UNITY_VERSION}-base-${UNITY_IMAGE_VERSION}"

echo "==> [run-unity-docker] Unity image: ${UNITY_IMAGE}"
echo "==> [run-unity-docker] Workspace: ${WORKSPACE_DIR}"
echo "==> [run-unity-docker] Test project: ${UNITY_TEST_PROJECT_DIR}"
echo "==> [run-unity-docker] Unity cache: ${UNITY_LICENSE_CACHE_DIR}"
echo "==> [run-unity-docker] Arguments: $*"
echo "==> [run-unity-docker] Timeout: ${UNITY_TIMEOUT}s"

# Safely escape arguments for passing through bash -c
# Using printf '%q' preserves argument boundaries and escapes shell metacharacters
if [[ $# -gt 0 ]]; then
    ESCAPED_ARGS=$(printf '%q ' "$@")
else
    ESCAPED_ARGS=""
fi

# Build the inner script that runs inside the Docker container.
# All credential env vars (UNITY_LICENSE, UNITY_SERIAL, UNITY_EMAIL, UNITY_PASSWORD)
# are passed via Docker -e flags and expanded inside the container only.
# Single-quoted heredoc sections ensure no host-side expansion of credentials.
INNER_SCRIPT='#!/usr/bin/env bash
set -euo pipefail

# Ensure cache directories exist and are writable inside container
mkdir -p /root/.local/share/unity3d /root/.config/unity3d 2>/dev/null || true

check_license_artifact() {
    local candidate
    for candidate in \
        "/root/.local/share/unity3d/Unity/Unity_lic.ulf" \
        "/root/.config/unity3d/Unity/Unity_lic.ulf" \
        "/root/.local/share/unity3d/Unity/UnityEntitlementLicense.xml" \
        "/root/.config/unity3d/Unity/UnityEntitlementLicense.xml"
    do
        if [[ -s "${candidate}" ]]; then
            echo "==> Using cached license artifact: ${candidate} (cache hit - skipping re-authentication)"
            return 0
        fi
    done
    return 1
}

require_license_artifact() {
    if ! check_license_artifact; then
        echo "ERROR: Unity license activation did not produce a usable local license artifact."
        echo "ERROR: Possible causes:"
        echo "  1. Incorrect email/password credentials"
        echo "  2. License file bound to a different machine (ULF files are machine-specific)"
        echo "  3. Unity licensing service rejected this activation for the current account/machine"
        echo "ERROR: See docs/guides/unity-devcontainer-licensing.md"
        exit 1
    fi
}

# ── xvfb setup (if requested) ───────────────────────────────────────────────
'

if [[ "${UNITY_USE_XVFB}" == "1" ]]; then
    echo "==> [run-unity-docker] xvfb mode enabled"
    INNER_SCRIPT+='
echo "==> Installing xvfb..."
apt-get update -qq && apt-get install -y -qq xvfb > /dev/null 2>&1
echo "==> xvfb installed."
USE_XVFB=1
'
else
    INNER_SCRIPT+='
USE_XVFB=0
'
fi

# ── License activation ──────────────────────────────────────────────────────
if [[ -n "${UNITY_EMAIL:-}" && -n "${UNITY_PASSWORD:-}" && -z "${UNITY_SERIAL:-}" ]]; then
    echo "==> [run-unity-docker] Using Personal license (online activation with email+password)"
    # Unity Personal must activate online. Unity no longer supports manual
    # activation for Personal, so .alf upload is not a valid fallback path.
    INNER_SCRIPT+='
echo "==> Attempting online activation with email+password..."
ONLINE_CONFIRMED=0
HARD_FAILURE=0
SOFT_FAILURE=0
MACHINE_NOT_REGISTERED=0
ONLINE_LOG_FILE="/root/.config/unity3d/.activation-$(date +%s%N).log"
ONLINE_OUTPUT=$(unity-editor -batchmode -nographics -quit \
    -username "${UNITY_EMAIL}" \
    -password "${UNITY_PASSWORD}" \
    -logFile /dev/stdout 2>&1) || true
echo "${ONLINE_OUTPUT}" | tee "${ONLINE_LOG_FILE}"
echo "==> Activation log saved: ${ONLINE_LOG_FILE}"

if echo "${ONLINE_OUTPUT}" | grep -Eqi "No license activation found for this computer|No ULF license found"; then
    MACHINE_NOT_REGISTERED=1
elif echo "${ONLINE_OUTPUT}" | grep -Eqi "Found 0 entitlement groups|com\.unity\.editor\.headless was not found|No valid Unity Editor license found|Token not found in cache|No (valid )?(Unity Editor )?license (found|available)"; then
    HARD_FAILURE=1
elif echo "${ONLINE_OUTPUT}" | grep -Eqi "invalid (user )?credentials|invalid username|bad credentials|authentication failed"; then
    HARD_FAILURE=1
elif echo "${ONLINE_OUTPUT}" | grep -Eqi "timeout|network|connection|temporar|DNS|refused|unreachable"; then
    SOFT_FAILURE=1
elif echo "${ONLINE_OUTPUT}" | grep -qiE "license activated|License updated successfully|Entitlement-based licensing initiated"; then
    ONLINE_CONFIRMED=1
fi

if [[ "${ONLINE_CONFIRMED}" -ne 1 ]]; then
    ACTIVATION_DONE=0

    if [[ "${MACHINE_NOT_REGISTERED}" -eq 1 ]]; then
        echo "==> Online activation failed: this machine has no Unity license registration."
        ULF_SUCCEEDED=0
        if [[ -n "${UNITY_LICENSE:-}" ]]; then
            echo "==> Trying .ulf file (in case it was generated for this machine)..."
            printf "%s\n" "${UNITY_LICENSE}" > /tmp/unity.ulf
            ULF_OUTPUT=$(unity-editor -batchmode -nographics -quit -manualLicenseFile /tmp/unity.ulf -logFile /dev/stdout 2>&1) || true
            echo "${ULF_OUTPUT}"
            rm -f /tmp/unity.ulf
            if check_license_artifact; then
                echo "==> .ulf activation succeeded."
                ULF_SUCCEEDED=1
            fi
        fi
        if [[ "${ULF_SUCCEEDED}" -eq 1 ]]; then
            ACTIVATION_DONE=1
        else
            echo ""
            echo "ERROR: Unity reported that this machine is not registered for the current account."
            echo "ERROR: Unity Personal does not support manual activation via .alf/.ulf upload."
            echo "ERROR: Use Unity Hub on a supported interactive machine to activate Personal,"
            echo "ERROR: or use a serial-based paid license if you need manual activation."
            echo "ERROR: If you already have a machine-matched .ulf file, place it at"
            echo "ERROR: .unity-secrets/license.ulf and rerun the command."
            echo "ERROR: See docs/guides/unity-devcontainer-licensing.md for supported paths."
            exit 1
        fi
    fi

    if [[ "${ACTIVATION_DONE}" -eq 0 ]]; then
        if [[ "${HARD_FAILURE}" -eq 1 ]]; then
            echo "ERROR: Online activation failed with a hard licensing rejection."
            echo "ERROR: Skipping .ulf fallback because this failure is not recoverable via local file activation."
            echo "ERROR: If logs contain Found 0 entitlement groups or com.unity.editor.headless was not found,"
            echo "ERROR: open a Unity support ticket and attach the activation log path shown above."
            echo "ERROR: See docs/guides/unity-devcontainer-licensing.md for troubleshooting steps."
            exit 1
        fi

        if [[ "${SOFT_FAILURE}" -eq 1 && -n "${UNITY_LICENSE:-}" ]]; then
            echo "==> Online activation had a transient failure. Falling back to .ulf file..."
            printf "%s\n" "${UNITY_LICENSE}" > /tmp/unity.ulf
            ULF_OUTPUT=$(unity-editor -batchmode -nographics -quit -manualLicenseFile /tmp/unity.ulf -logFile /dev/stdout 2>&1) || true
            echo "${ULF_OUTPUT}"
            rm -f /tmp/unity.ulf
            require_license_artifact
        elif [[ -n "${UNITY_LICENSE:-}" ]]; then
            echo "==> Online activation was not confirmed. Falling back to .ulf file..."
            printf "%s\n" "${UNITY_LICENSE}" > /tmp/unity.ulf
            ULF_OUTPUT=$(unity-editor -batchmode -nographics -quit -manualLicenseFile /tmp/unity.ulf -logFile /dev/stdout 2>&1) || true
            echo "${ULF_OUTPUT}"
            rm -f /tmp/unity.ulf
            require_license_artifact
        else
            echo "ERROR: Online activation failed and no .ulf fallback available."
            echo "ERROR: Common reasons for activation failure:"
            echo "  - Invalid email/password credentials"
            echo "  - Network connectivity issues"
            echo "  - Unity returned no valid license for this machine/account"
            echo "ERROR: See docs/guides/unity-devcontainer-licensing.md for troubleshooting steps."
            exit 1
        fi
    fi
else
    require_license_artifact
fi
echo "==> License activation complete."
'
elif [[ -n "${UNITY_LICENSE:-}" ]]; then
    echo "==> [run-unity-docker] Using manual .ulf license file"
    # Manual .ulf path for machine-matched licenses. This is not a supported
    # Unity Personal activation flow, but remains valid for serial/manual licenses.
    INNER_SCRIPT+='
echo "==> Activating Unity with manual .ulf license..."
printf "%s\n" "${UNITY_LICENSE}" > /tmp/unity.ulf
ULF_OUTPUT=$(unity-editor -batchmode -nographics -quit -manualLicenseFile /tmp/unity.ulf -logFile /dev/stdout 2>&1) || true
echo "${ULF_OUTPUT}"
rm -f /tmp/unity.ulf
require_license_artifact
echo "==> License activation complete."
'
elif [[ -n "${UNITY_SERIAL:-}" ]]; then
    if [[ -z "${UNITY_EMAIL:-}" || -z "${UNITY_PASSWORD:-}" ]]; then
        echo "ERROR: UNITY_SERIAL requires UNITY_EMAIL and UNITY_PASSWORD to be set."
        exit 1
    fi
    echo "==> [run-unity-docker] Using Pro license (serial)"
    # Serial, email, and password are available inside the container via Docker -e flags.
    INNER_SCRIPT+='
echo "==> Activating Unity with Pro serial license..."
SERIAL_OUTPUT=$(unity-editor -batchmode -nographics -quit \
    -serial "${UNITY_SERIAL}" \
    -username "${UNITY_EMAIL}" \
    -password "${UNITY_PASSWORD}" \
    -logFile /dev/stdout 2>&1) || true
echo "${SERIAL_OUTPUT}"
require_license_artifact
echo "==> License activation complete."
'
else
    echo "==> No activation credentials provided. Checking for cached Unity license artifacts..."
    INNER_SCRIPT+=$'\nrequire_license_artifact\n'
fi

# ── Main Unity command ──────────────────────────────────────────────────────
# The escaped arguments are safe to interpolate here because printf '%q' was used.
# xvfb-run is invoked via an if/else block to avoid word-splitting issues with
# --server-args (the space-containing argument cannot be stored safely in a variable).
INNER_SCRIPT+="
echo '==> Running Unity command...'
EXIT_CODE=0
if [[ \"\${USE_XVFB:-0}\" == \"1\" ]]; then
    xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \\
        unity-editor ${ESCAPED_ARGS} || EXIT_CODE=\$?
else
    unity-editor ${ESCAPED_ARGS} || EXIT_CODE=\$?
fi
"

# ── License return (serial only) ────────────────────────────────────────────
if [[ -n "${UNITY_SERIAL:-}" ]]; then
    INNER_SCRIPT+='
echo "==> Returning Pro serial license..."
unity-editor -batchmode -nographics -quit -returnlicense -logFile - || true
echo "==> License returned."
'
fi

# ── Exit with Unity exit code ───────────────────────────────────────────────
INNER_SCRIPT+='
echo "==> Unity command finished with exit code: ${EXIT_CODE}"
exit ${EXIT_CODE}
'

# Ensure test project directory exists
mkdir -p "${UNITY_TEST_PROJECT_DIR}"
mkdir -p "${UNITY_LICENSE_CACHE_LOCAL_DIR}" "${UNITY_LICENSE_CACHE_CONFIG_DIR}"
rm -f "${UNITY_LICENSE_CACHE_CONFIG_DIR}/.alf-generated-this-run"

echo "==> [run-unity-docker] Starting Docker container..."

# Run with timeout to prevent indefinite hangs.
# The workspace is mounted read-only (/workspace:ro) for safety.
# The test project directory is mounted read-write (/project) for compilation artifacts.
DOCKER_EXIT=0
timeout "${UNITY_TIMEOUT}" docker run --rm \
    -v "${WORKSPACE_DIR}:/workspace:ro" \
    -v "${UNITY_TEST_PROJECT_DIR}:/project" \
    -v "${UNITY_LICENSE_CACHE_LOCAL_DIR}:/root/.local/share/unity3d" \
    -v "${UNITY_LICENSE_CACHE_CONFIG_DIR}:/root/.config/unity3d" \
    -e UNITY_LICENSE \
    -e UNITY_SERIAL \
    -e UNITY_EMAIL \
    -e UNITY_PASSWORD \
    -w /project \
    "${UNITY_IMAGE}" \
    bash -c "${INNER_SCRIPT}" || DOCKER_EXIT=$?

# ── Post-run: copy .alf to .unity-secrets/ if machine registration needed ──
if [[ -f "${UNITY_LICENSE_CACHE_CONFIG_DIR}/manual-activation.alf" && -f "${UNITY_LICENSE_CACHE_CONFIG_DIR}/.alf-generated-this-run" ]]; then
    mkdir -p "${SECRETS_DIR}"
    cp "${UNITY_LICENSE_CACHE_CONFIG_DIR}/manual-activation.alf" "${SECRETS_DIR}/manual-activation.alf"
    echo ""
    echo "==> [run-unity-docker] Manual activation file copied to .unity-secrets/manual-activation.alf"
    echo "==> [run-unity-docker] To activate this machine, follow these steps:"
    echo "==>   1. The activation file is at: .unity-secrets/manual-activation.alf"
    echo "==>   2. Upload it at https://license.unity3d.com/manual"
    echo "==>   3. Download the .ulf file and save it as: .unity-secrets/license.ulf"
    echo "==>   4. Run: npm run unity:retry-license"
fi

rm -f "${UNITY_LICENSE_CACHE_CONFIG_DIR}/.alf-generated-this-run"

exit "${DOCKER_EXIT}"
