#!/bin/sh
set -eu

export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:${PATH:-}"

HOST_PATH="${1:-}"
RUNTIME_DIR="${2:-}"
COMPONENT_DIR="${3:-}"
[ "$#" -lt 3 ] || shift 3

if [ -z "$HOST_PATH" ]; then
    echo "missing host path" >&2
    exit 127
fi

app_support_dir() {
    if [ -n "${SLICER_LINUX_RUNTIME_MAC_APP_SUPPORT_DIR:-}" ]; then
        printf '%s\n' "$SLICER_LINUX_RUNTIME_MAC_APP_SUPPORT_DIR"
    else
        printf '%s\n' "$HOME/Library/Application Support/BambuStudio_OrcaSlicer/slicer-linux-runtime"
    fi
}

runtime_dir_default() {
    printf '%s/runtime\n' "$(app_support_dir)"
}

shell_quote() {
    printf "'"
    printf '%s' "$1" | sed "s/'/'\\\\''/g"
    printf "'"
}

trim_file() {
    path="$1"
    [ -f "$path" ] || return 1
    LC_ALL=C awk 'NR == 1 { gsub(/\r/, ""); sub(/^[[:space:]]+/, ""); sub(/[[:space:]]+$/, ""); print }' "$path"
}

find_limactl() {
    if [ -n "${SLICER_LINUX_RUNTIME_LIMACTL:-}" ] && [ -x "$SLICER_LINUX_RUNTIME_LIMACTL" ]; then
        printf '%s\n' "$SLICER_LINUX_RUNTIME_LIMACTL"
        return 0
    fi

    local_lima="$(app_support_dir)/lima/bin/limactl"
    if [ -x "$local_lima" ]; then
        printf '%s\n' "$local_lima"
        return 0
    fi

    if command -v limactl >/dev/null 2>&1; then
        command -v limactl
        return 0
    fi

    for candidate in /opt/homebrew/bin/limactl /usr/local/bin/limactl; do
        if [ -x "$candidate" ]; then
            printf '%s\n' "$candidate"
            return 0
        fi
    done
    return 1
}

if [ -z "$RUNTIME_DIR" ]; then
    RUNTIME_DIR="${SLICER_LINUX_RUNTIME_MAC_RUNTIME_DIR:-$(runtime_dir_default)}"
fi
if [ -z "$COMPONENT_DIR" ]; then
    COMPONENT_DIR="${SLICER_LINUX_RUNTIME_COMPONENT_DIR:-$RUNTIME_DIR}"
fi

# Keep the downloaded optional component in the persistent macOS runtime cache.
# The Linux guest receives a separate, architecture-clean copy below.
sync_component_payload() {
    [ -d "$COMPONENT_DIR" ] || return 0
    mkdir -p "$RUNTIME_DIR"
    for path in "$COMPONENT_DIR"/*; do
        [ -f "$path" ] || continue
        base=$(basename "$path")
        case "$base" in
            libbambu_networking.so|libBambuSource.so|liblive555.so|libagora_rtc_sdk.so|libagora-fdkaac.so|linux_component_manifest.json)
                if [ ! -f "$RUNTIME_DIR/$base" ] || ! cmp -s "$path" "$RUNTIME_DIR/$base"; then
                    tmp_path=$(mktemp "$RUNTIME_DIR/.payload.XXXXXX")
                    cp "$path" "$tmp_path" || { rm -f "$tmp_path"; return 1; }
                    case "$base" in *.so|*.so.*) chmod 755 "$tmp_path" 2>/dev/null || true ;; esac
                    mv -f "$tmp_path" "$RUNTIME_DIR/$base" || { rm -f "$tmp_path"; return 1; }
                fi
                ;;
        esac
    done
}

sync_component_payload

INSTANCE="${SLICER_LINUX_RUNTIME_MAC_LIMA_INSTANCE:-}"
if [ -z "$INSTANCE" ]; then
    INSTANCE=$(trim_file "$COMPONENT_DIR/slicer_linux_runtime_lima_instance.txt" || true)
fi
if [ -z "$INSTANCE" ]; then
    INSTANCE="slicer-linux-runtime"
fi

LIMACTL=$(find_limactl || true)
if [ -z "$LIMACTL" ]; then
    echo "limactl not found" >&2
    exit 127
fi

APP_SUPPORT_DIR=$(app_support_dir)
LOG_DIR="$APP_SUPPORT_DIR/logs"
LOG_FILE="$LOG_DIR/linux-runtime-host.log"
mkdir -p "$LOG_DIR"

STATUS_OUTPUT=$("$LIMACTL" list --format '{{.Status}}' "$INSTANCE" 2>>"$LOG_FILE" || true)
STATUS=$(LC_ALL=C awk 'NR == 1 { gsub(/\r/, ""); print }' <<EOF_STATUS
$STATUS_OUTPUT
EOF_STATUS
)
if [ "$STATUS" != "Running" ]; then
    if ! "$LIMACTL" start "$INSTANCE" >>"$LOG_FILE" 2>&1; then
        echo "failed to start Lima instance: $INSTANCE" >&2
        exit 127
    fi
fi

SSH_CONFIG_OUTPUT=$("$LIMACTL" list --format '{{.SSHConfigFile}}' "$INSTANCE" 2>>"$LOG_FILE" || true)
SSH_CONFIG=$(LC_ALL=C awk 'NR == 1 { gsub(/\r/, ""); print }' <<EOF_SSH_CONFIG
$SSH_CONFIG_OUTPUT
EOF_SSH_CONFIG
)
if [ -z "$SSH_CONFIG" ] || [ ! -f "$SSH_CONFIG" ]; then
    echo "Lima SSH configuration is unavailable for instance: $INSTANCE" >&2
    exit 127
fi
SSH_HOST=$(awk '/^Host[[:space:]]+/ && !found { value=$2; found=1 } END { if (found) print value }' "$SSH_CONFIG")
if [ -z "$SSH_HOST" ]; then
    SSH_HOST="lima-$INSTANCE"
fi

SSH_BIN="${SLICER_LINUX_RUNTIME_SSH:-/usr/bin/ssh}"
if [ ! -x "$SSH_BIN" ]; then
    echo "ssh client not found: $SSH_BIN" >&2
    exit 127
fi

ssh_guest_control() {
    "$SSH_BIN" -F "$SSH_CONFIG" -T -n \
        -o BatchMode=yes \
        -o LogLevel=ERROR \
        -o ConnectTimeout=20 \
        -o ServerAliveInterval=15 \
        -o ServerAliveCountMax=3 \
        "$SSH_HOST" "$@"
}

ssh_guest_stream() {
    "$SSH_BIN" -F "$SSH_CONFIG" -T \
        -o BatchMode=yes \
        -o LogLevel=ERROR \
        -o ConnectTimeout=20 \
        -o ServerAliveInterval=15 \
        -o ServerAliveCountMax=3 \
        "$SSH_HOST" "$@"
}

disable_guest_rosetta_aot_cache() {
    disable_cmd='set -eu
[ -x /mnt/lima-rosetta/rosetta ] || exit 0
case "$(uname -m 2>/dev/null || true)" in aarch64|arm64) ;; *) exit 0 ;; esac
if [ "$(id -u)" -eq 0 ]; then SUDO=""; elif command -v sudo >/dev/null 2>&1; then SUDO="sudo -n"; else echo "sudo is required to disable Rosetta AOT caching" >&2; exit 1; fi
marker_tmp=$(mktemp)
dropin_tmp=$(mktemp)
trap "rm -f \"$marker_tmp\" \"$dropin_tmp\"" EXIT HUP INT TERM
printf "%s\n" "disabled-for-protected-vendor-elf" > "$marker_tmp"
printf "%s\n" "[Unit]" "ConditionPathExists=!/etc/orcastudio-rosetta-aot-disabled" > "$dropin_tmp"
$SUDO install -m 0644 "$marker_tmp" /etc/orcastudio-rosetta-aot-disabled
if command -v systemctl >/dev/null 2>&1 && systemctl cat rosettad.service >/dev/null 2>&1; then
  $SUDO install -d -m 0755 /etc/systemd/system/rosettad.service.d
  $SUDO install -m 0644 "$dropin_tmp" /etc/systemd/system/rosettad.service.d/10-orcastudio-disable-aot.conf
  $SUDO systemctl daemon-reload
  $SUDO systemctl stop rosettad.service
fi
if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet rosettad.service 2>/dev/null; then echo "rosettad is still active" >&2; exit 1; fi
rm -f "$marker_tmp" "$dropin_tmp"
trap - EXIT HUP INT TERM
$SUDO rm -f /run/rosettad/rosetta.sock /var/cache/rosettad/uds/rosetta.sock
if [ -d /var/cache/rosettad ]; then $SUDO find /var/cache/rosettad -xdev -mindepth 1 -delete; fi
[ ! -S /run/rosettad/rosetta.sock ]
[ ! -S /var/cache/rosettad/uds/rosetta.sock ]'
    if ! ssh_guest_control "/bin/sh -c $(shell_quote "$disable_cmd")" >>"$LOG_FILE" 2>&1; then
        echo "failed to disable Rosetta AOT caching in the Lima guest" >&2
        return 1
    fi
}

# Rosetta is registered in the ARM guest through binfmt_misc. Run x86_64 ELF
# files normally from the guest filesystem and let the guest's amd64 libc loader
# handle them. Do not execute the private ld-linux directly and do not mmap the
# translated process from the macOS VirtioFS mount - both combinations produced
# SIGBUS on real Apple Silicon hardware.
GUEST_RUNTIME_BASE_REL=".local/share/bambustudio-orcaslicer/linux-runtime-payloads"
hash_named_file() {
    file="$1"
    name="$2"
    [ -f "$file" ] || return 0
    digest=$(shasum -a 256 "$file" | awk '{print $1}') || return 1
    printf '%s  %s\n' "$digest" "$name"
}

payload_marker() {
    {
        printf '%s\n' 'orcastudio-linux-guest-payload-v3-content-addressed'
        if [ -f "$RUNTIME_DIR/runtime-files.sha256" ]; then
            cat "$RUNTIME_DIR/runtime-files.sha256"
        fi
        for name in \
            slicer_linux_runtime_host \
            slicer_linux_runtime_host_abi1 \
            slicer_linux_runtime_host_abi0 \
            liborcastudio_rosetta_splitlock_compat.so \
            slicer_linux_auth_browser \
            slicer_linux_auth_browser_x86_64 \
            slicer_linux_auth_browser_aarch64 \
            run_auth_browser.sh \
            ca-certificates.crt \
            slicer_base64.cer; do
            hash_named_file "$RUNTIME_DIR/$name" "$name"
        done
        for name in \
            libbambu_networking.so \
            libBambuSource.so \
            liblive555.so \
            libagora_rtc_sdk.so \
            libagora-fdkaac.so \
            linux_component_manifest.json; do
            if [ -f "$COMPONENT_DIR/$name" ]; then
                hash_named_file "$COMPONENT_DIR/$name" "$name"
            else
                hash_named_file "$RUNTIME_DIR/$name" "$name"
            fi
        done
    } | shasum -a 256 | awk '{print $1}'
}

copy_to_stage() {
    src="$1"
    dst="$2"
    [ -f "$src" ] || return 0
    cp -f "$src" "$dst"
}

PAYLOAD_MARKER=$(payload_marker) || {
    echo "failed to calculate Linux guest payload marker" >&2
    exit 127
}
GUEST_RUNTIME_REL="$GUEST_RUNTIME_BASE_REL/$PAYLOAD_MARKER"

sync_guest_payload() {
    marker="$PAYLOAD_MARKER"
    check_cmd='root="$HOME/'"$GUEST_RUNTIME_REL"'"; test -x "$root/slicer_linux_runtime_host" && test -x "$root/slicer_linux_runtime_host_abi1" && test -f "$root/liborcastudio_rosetta_splitlock_compat.so" && test -x "$root/slicer_linux_auth_browser_x86_64" && test -x "$root/slicer_linux_auth_browser_aarch64" && test -x "$root/run_auth_browser.sh" && cat "$root/.payload-marker" 2>/dev/null || true'
    remote_marker=$(ssh_guest_control "/bin/sh -c $(shell_quote "$check_cmd")" 2>>"$LOG_FILE" | tr -d '\r\n' || true)
    if [ "$remote_marker" = "$marker" ]; then
        return 0
    fi

    local_stage=$(mktemp -d "$APP_SUPPORT_DIR/.guest-payload.XXXXXX") || return 1
    trap 'rm -rf "${local_stage:-}"' EXIT HUP INT TERM

    for name in \
        slicer_linux_runtime_host \
        slicer_linux_runtime_host_abi1 \
        slicer_linux_runtime_host_abi0 \
        liborcastudio_rosetta_splitlock_compat.so \
        slicer_linux_auth_browser \
        slicer_linux_auth_browser_x86_64 \
        slicer_linux_auth_browser_aarch64 \
        run_auth_browser.sh \
        ca-certificates.crt \
        slicer_base64.cer; do
        copy_to_stage "$RUNTIME_DIR/$name" "$local_stage/$name"
    done
    for name in \
        libbambu_networking.so \
        libBambuSource.so \
        liblive555.so \
        libagora_rtc_sdk.so \
        libagora-fdkaac.so \
        linux_component_manifest.json; do
        if [ -f "$COMPONENT_DIR/$name" ]; then
            copy_to_stage "$COMPONENT_DIR/$name" "$local_stage/$name"
        else
            copy_to_stage "$RUNTIME_DIR/$name" "$local_stage/$name"
        fi
    done

    printf '%s\n' "$marker" > "$local_stage/.payload-marker"
    chmod 755 \
        "$local_stage/slicer_linux_runtime_host" \
        "$local_stage/slicer_linux_runtime_host_abi1" \
        "$local_stage/slicer_linux_auth_browser" \
        "$local_stage/slicer_linux_auth_browser_x86_64" \
        "$local_stage/slicer_linux_auth_browser_aarch64" \
        "$local_stage/run_auth_browser.sh"
    [ ! -f "$local_stage/slicer_linux_runtime_host_abi0" ] || chmod 755 "$local_stage/slicer_linux_runtime_host_abi0"
    chmod 755 "$local_stage"/*.so 2>/dev/null || true

    # Intentionally exclude ld-linux, libc, libstdc++ and the bundled x86 support
    # closure. The guest's coherent amd64 multiarch packages must satisfy those
    # dependencies; mixing the private Ubuntu userspace with Rosetta caused SIGBUS.
    sync_cmd='set -eu; base="$HOME/'"$GUEST_RUNTIME_BASE_REL"'"; keep='"$PAYLOAD_MARKER"'; root="$base/$keep"; tmp="${root}.tmp.$$"; old="${root}.old.$$"; rm -rf "$tmp" "$old"; mkdir -p "$tmp"; tar -xf - -C "$tmp"; test -x "$tmp/slicer_linux_runtime_host"; test -x "$tmp/slicer_linux_runtime_host_abi1"; test -f "$tmp/liborcastudio_rosetta_splitlock_compat.so"; chmod 755 "$tmp"/slicer_linux_runtime_host "$tmp"/slicer_linux_runtime_host_abi1 "$tmp"/slicer_linux_auth_browser "$tmp"/slicer_linux_auth_browser_x86_64 "$tmp"/slicer_linux_auth_browser_aarch64 "$tmp"/run_auth_browser.sh; chmod 755 "$tmp"/*.so 2>/dev/null || true; if [ -d "$root" ]; then mv "$root" "$old"; fi; if mv "$tmp" "$root"; then rm -rf "$old"; rm -rf "$HOME/.local/share/bambustudio-orcaslicer/linux-runtime"; else rc=$?; rm -rf "$tmp"; if [ -d "$old" ]; then mv "$old" "$root"; fi; exit "$rc"; fi'
    if ! (cd "$local_stage" && /usr/bin/tar -cf - .) | ssh_guest_stream "/bin/sh -c $(shell_quote "$sync_cmd")" >>"$LOG_FILE" 2>&1; then
        rm -rf "$local_stage"
        trap - EXIT HUP INT TERM
        echo "failed to synchronize Linux runtime into the Lima guest filesystem" >&2
        return 1
    fi

    rm -rf "$local_stage"
    trap - EXIT HUP INT TERM
    return 0
}

if ! disable_guest_rosetta_aot_cache; then
    exit 127
fi

if ! sync_guest_payload; then
    exit 127
fi

HOST_NOVNC_PORT=${SLICER_LINUX_RUNTIME_AUTH_HOST_NOVNC_PORT:-}
GUEST_NOVNC_PORT=${SLICER_LINUX_RUNTIME_AUTH_NOVNC_PORT:-$HOST_NOVNC_PORT}
GUEST_VNC_PORT=${SLICER_LINUX_RUNTIME_AUTH_VNC_PORT:-}
FORWARD_ENABLED=0
if [ -n "$HOST_NOVNC_PORT$GUEST_NOVNC_PORT$GUEST_VNC_PORT" ]; then
    case "$HOST_NOVNC_PORT:$GUEST_NOVNC_PORT:$GUEST_VNC_PORT" in
        *[!0-9:]*|:*|*::*|*:)
            echo "invalid Linux browser transport ports" >&2
            exit 127
            ;;
    esac
    if [ "$HOST_NOVNC_PORT" -le 0 ] || [ "$HOST_NOVNC_PORT" -gt 65535 ] ||
       [ "$GUEST_NOVNC_PORT" -le 0 ] || [ "$GUEST_NOVNC_PORT" -gt 65535 ] ||
       [ "$GUEST_VNC_PORT" -le 0 ] || [ "$GUEST_VNC_PORT" -gt 65535 ] ||
       [ "$GUEST_NOVNC_PORT" -eq "$GUEST_VNC_PORT" ]; then
        echo "invalid Linux browser transport port range" >&2
        exit 127
    fi
    FORWARD_ENABLED=1
fi

if [ -n "${SLICER_LINUX_RUNTIME_EXPECTED_ABI_VERSION:-}" ]; then
    EXPECTED_ABI_COMMAND="export SLICER_LINUX_RUNTIME_EXPECTED_ABI_VERSION=$(shell_quote "$SLICER_LINUX_RUNTIME_EXPECTED_ABI_VERSION");"
else
    EXPECTED_ABI_COMMAND="unset SLICER_LINUX_RUNTIME_EXPECTED_ABI_VERSION;"
fi

CMD='cd /; if [ "$(uname -s)" != Linux ]; then echo "not running inside Linux guest" >&2; exit 127; fi; GUEST_RUNTIME_DIR="$HOME/'"$GUEST_RUNTIME_REL"'"; test -x "$GUEST_RUNTIME_DIR/slicer_linux_runtime_host" || { echo "staged Linux runtime missing" >&2; exit 127; }; GUEST_ARCH=$(uname -m); case "$GUEST_ARCH" in aarch64|arm64) AUTH_BROWSER="$GUEST_RUNTIME_DIR/slicer_linux_auth_browser_aarch64" ;; x86_64|amd64) if [ -x "$GUEST_RUNTIME_DIR/slicer_linux_auth_browser_x86_64" ]; then AUTH_BROWSER="$GUEST_RUNTIME_DIR/slicer_linux_auth_browser_x86_64"; else AUTH_BROWSER="$GUEST_RUNTIME_DIR/slicer_linux_auth_browser"; fi ;; *) echo "unsupported Lima guest architecture: $GUEST_ARCH" >&2; exit 127 ;; esac; test -x "$AUTH_BROWSER" || { echo "authentication browser missing for guest architecture $GUEST_ARCH: $AUTH_BROWSER" >&2; exit 127; }; export SLICER_LINUX_RUNTIME_AUTH_BROWSER="$AUTH_BROWSER"; export SLICER_LINUX_RUNTIME_COMPONENT_DIR="$GUEST_RUNTIME_DIR"; export SLICER_LINUX_RUNTIME_COMPONENT_SO="$GUEST_RUNTIME_DIR/libbambu_networking.so"; export SLICER_LINUX_RUNTIME_SOURCE_SO="$GUEST_RUNTIME_DIR/libBambuSource.so"; export SLICER_LINUX_RUNTIME_MEDIA_SO="$GUEST_RUNTIME_DIR/liblive555.so"; export SLICER_LINUX_RUNTIME_REQUIRE_LINUX_GUEST=1; export SLICER_LINUX_RUNTIME_PREFER_SYSTEM_LOADER=1; export SLICER_LINUX_RUNTIME_DISABLE_ABI0_FALLBACK=1;'
CMD="$CMD $EXPECTED_ABI_COMMAND export SLICER_LINUX_RUNTIME_REQUIRE_COMPATIBLE_HOST=$(shell_quote "${SLICER_LINUX_RUNTIME_REQUIRE_COMPATIBLE_HOST:-0}");"
if [ "$FORWARD_ENABLED" -eq 1 ]; then
    CMD="$CMD export SLICER_LINUX_RUNTIME_AUTH_HOST_NOVNC_PORT=$(shell_quote "$HOST_NOVNC_PORT"); export SLICER_LINUX_RUNTIME_AUTH_NOVNC_PORT=$(shell_quote "$GUEST_NOVNC_PORT"); export SLICER_LINUX_RUNTIME_AUTH_VNC_PORT=$(shell_quote "$GUEST_VNC_PORT");"
fi
CMD="$CMD if [ -s \"\$GUEST_RUNTIME_DIR/ca-certificates.crt\" ] && [ \"\$(grep -c -- '-----BEGIN CERTIFICATE-----' \"\$GUEST_RUNTIME_DIR/ca-certificates.crt\" 2>/dev/null || true)\" -ge 50 ]; then export SLICER_LINUX_RUNTIME_CA_BUNDLE=\"\$GUEST_RUNTIME_DIR/ca-certificates.crt\"; export SSL_CERT_FILE=\"\$SLICER_LINUX_RUNTIME_CA_BUNDLE\"; export CURL_CA_BUNDLE=\"\$SLICER_LINUX_RUNTIME_CA_BUNDLE\"; elif [ -s /etc/ssl/certs/ca-certificates.crt ]; then export SLICER_LINUX_RUNTIME_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt; export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt; export CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt; else echo 'valid CA certificate bundle not found in Linux guest' >&2; exit 127; fi; if [ -d /etc/ssl/certs ]; then export SSL_CERT_DIR=/etc/ssl/certs; fi; exec /bin/sh \"\$GUEST_RUNTIME_DIR/slicer_linux_runtime_host\""
for arg in "$@"; do
    CMD="$CMD $(shell_quote "$arg")"
done
REMOTE_COMMAND="/bin/sh -c $(shell_quote "$CMD")"

{
    printf '%s\n' '---- slicer linux runtime launch ----'
    date
    printf 'instance=%s\n' "$INSTANCE"
    printf 'ssh_config=%s\n' "$SSH_CONFIG"
    printf 'ssh_host=%s\n' "$SSH_HOST"
    printf 'host_path=%s\n' "$HOST_PATH"
    printf 'runtime_dir=%s\n' "$RUNTIME_DIR"
    printf 'component_dir=%s\n' "$COMPONENT_DIR"
    printf 'guest_runtime=%s\n' "$GUEST_RUNTIME_REL"
    printf 'expected_abi=%s\n' "${SLICER_LINUX_RUNTIME_EXPECTED_ABI_VERSION:-}"
    printf 'forward_enabled=%s host_novnc_port=%s guest_novnc_port=%s guest_vnc_port=%s\n' \
        "$FORWARD_ENABLED" "$HOST_NOVNC_PORT" "$GUEST_NOVNC_PORT" "$GUEST_VNC_PORT"
} >>"$LOG_FILE" 2>/dev/null || true

if [ "$FORWARD_ENABLED" -eq 1 ]; then
    exec "$SSH_BIN" -F "$SSH_CONFIG" -T \
        -o BatchMode=yes \
        -o LogLevel=ERROR \
        -o ConnectTimeout=20 \
        -o ExitOnForwardFailure=yes \
        -o ServerAliveInterval=15 \
        -o ServerAliveCountMax=3 \
        -L "127.0.0.1:$HOST_NOVNC_PORT:127.0.0.1:$GUEST_NOVNC_PORT" \
        "$SSH_HOST" "$REMOTE_COMMAND"
fi

exec "$SSH_BIN" -F "$SSH_CONFIG" -T \
    -o BatchMode=yes \
    -o LogLevel=ERROR \
    -o ConnectTimeout=20 \
    -o ServerAliveInterval=15 \
    -o ServerAliveCountMax=3 \
    "$SSH_HOST" "$REMOTE_COMMAND"
