# Pass args through to `craftos`. Prefer `just trapos-exec '<lua>'` for
# automated probes that must not hang the terminal.
# Launch the TrapOS dev environment in CraftOS-PC with repo-local data
# (.craftos/) and read-only repo mounts. See ADR-0005.
[positional-arguments]
trapos *args: check-install stage
    #!/usr/bin/env bash
    set -euo pipefail
    repo='{{justfile_directory()}}'
    argv=(--directory "$repo/.craftos")
    if [ "$(uname -s)" = "Darwin" ]; then
        argv+=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
    fi
    argv+=(--mount-ro "/trapos=$repo")
    for dir in apis programs daemons startup tests data; do
        if [ -d "$repo/.stage/$dir" ]; then
            argv+=(--mount-ro "/$dir=$repo/.stage/$dir")
        fi
    done
    exec craftos "${argv[@]}" "$@"

# Pass args through to a fresh, vanilla `craftos` with no TrapOS mounts.
# Persistent state lives under .craftos-vanilla/. Useful for probes that
# should not see TrapOS files (e.g. verifying upstream CC:Tweaked behavior)
# and as the base for `just trapos-install`. See ADR-0005.
[positional-arguments]
craftos *args: check-install
    #!/usr/bin/env bash
    set -euo pipefail
    repo='{{justfile_directory()}}'
    argv=(--directory "$repo/.craftos-vanilla")
    if [ "$(uname -s)" = "Darwin" ]; then
        argv+=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
    fi
    exec craftos "${argv[@]}" "$@"

# Safely run a Lua snippet in the TrapOS dev environment. The wrapper always
# shuts the machine down after normal completion or Lua errors, while the host
# watchdog catches snippets that block before reaching shutdown.
[positional-arguments]
trapos-exec code: stage
    #!/usr/bin/env bash
    set -uo pipefail
    repo='{{justfile_directory()}}'
    timeout_seconds="${TRAPOS_HEADLESS_TIMEOUT_SECONDS:-10}"
    case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAPOS_HEADLESS_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac
    if [ "$timeout_seconds" -lt 1 ]; then printf '%s\n' 'TRAPOS_HEADLESS_TIMEOUT_SECONDS must be >= 1' >&2; exit 1; fi
    rom_arg=()
    if [ "$(uname -s)" = "Darwin" ]; then
        rom_arg=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
    fi
    data_dir="$(mktemp -d)"
    stage_dir="$(mktemp -d)"
    tmp="$(mktemp)"
    output_path="$data_dir/computer/0/headless-output"
    status_path="$data_dir/computer/0/headless-status"
    runner="$stage_dir/exec.lua"
    {
        printf '%s\n' 'local output = fs.open("/headless-output", "w")'
        printf '%s\n' 'local function emitLine(...)'
        printf '%s\n' '  for i = 1, select("#", ...) do'
        printf '%s\n' '    if i > 1 then output.write("\t") end'
        printf '%s\n' '    output.write(tostring(select(i, ...)))'
        printf '%s\n' '  end'
        printf '%s\n' '  output.write("\n")'
        printf '%s\n' 'end'
        printf '%s\n' 'print = emitLine'
        printf '%s\n' 'write = function(value) output.write(tostring(value)); end'
        printf '%s\n' 'local function main()'
        printf '%s\n' "$1"
        printf '%s\n' 'end'
        printf '%s\n' 'local ok, err = xpcall(main, debug.traceback)'
        printf '%s\n' 'if not ok then'
        printf '%s\n' '  output.writeLine(err)'
        printf '%s\n' 'end'
        printf '%s\n' 'output.close()'
        printf '%s\n' 'local status = fs.open("/headless-status", "w")'
        printf '%s\n' 'status.writeLine(ok and "OK" or "FAIL")'
        printf '%s\n' 'status.close()'
        printf '%s\n' 'os.shutdown()'
    } > "$runner"
    mount_arg=(--mount-ro "/trapos=$repo" --mount-ro "/apis=$repo/.stage/apis" --mount-ro "/programs=$repo/.stage/programs" --mount-ro "/daemons=$repo/.stage/daemons" --mount-ro "/startup=$repo/.stage/startup" --mount-ro "/tests=$repo/.stage/tests" --mount-ro "/data=$repo/.stage/data" --mount-ro "/headless=$stage_dir")
    craftos --directory "$data_dir" --headless "${rom_arg[@]}" "${mount_arg[@]}" --exec "shell.run('/headless/exec.lua')" >"$tmp" 2>&1 &
    pid="$!"
    ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) &
    watchdog="$!"
    wait "$pid" >/dev/null 2>&1
    status="$?"
    kill "$watchdog" >/dev/null 2>&1 || true
    wait "$watchdog" >/dev/null 2>&1 || true
    red=$(printf '\033[31m'); reset=$(printf '\033[0m')
    if [ -f "$status_path" ] && grep -q '^OK$' "$status_path"; then
        if [ -f "$output_path" ]; then cat "$output_path"; fi
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
    else
        if [ "$status" -eq 143 ]; then
            printf '%s\n' "${red}FAIL${reset} TrapOS headless exec timed out after ${timeout_seconds}s" >&2
        else
            printf '%s\n' "${red}FAIL${reset} TrapOS headless exec failed" >&2
        fi
        if [ -f "$output_path" ]; then
            cat "$output_path" >&2
        else
            cat "$tmp" >&2
        fi
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
        exit 1
    fi

# Safely run a Lua snippet in vanilla CraftOS-PC with no TrapOS mounts.
[positional-arguments]
craftos-exec code:
    #!/usr/bin/env bash
    set -uo pipefail
    repo='{{justfile_directory()}}'
    timeout_seconds="${TRAPOS_HEADLESS_TIMEOUT_SECONDS:-10}"
    case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAPOS_HEADLESS_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac
    if [ "$timeout_seconds" -lt 1 ]; then printf '%s\n' 'TRAPOS_HEADLESS_TIMEOUT_SECONDS must be >= 1' >&2; exit 1; fi
    rom_arg=()
    if [ "$(uname -s)" = "Darwin" ]; then
        rom_arg=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
    fi
    data_dir="$(mktemp -d)"
    stage_dir="$(mktemp -d)"
    tmp="$(mktemp)"
    output_path="$data_dir/computer/0/headless-output"
    status_path="$data_dir/computer/0/headless-status"
    runner="$stage_dir/exec.lua"
    {
        printf '%s\n' 'local output = fs.open("/headless-output", "w")'
        printf '%s\n' 'local function emitLine(...)'
        printf '%s\n' '  for i = 1, select("#", ...) do'
        printf '%s\n' '    if i > 1 then output.write("\t") end'
        printf '%s\n' '    output.write(tostring(select(i, ...)))'
        printf '%s\n' '  end'
        printf '%s\n' '  output.write("\n")'
        printf '%s\n' 'end'
        printf '%s\n' 'print = emitLine'
        printf '%s\n' 'write = function(value) output.write(tostring(value)); end'
        printf '%s\n' 'local function main()'
        printf '%s\n' "$1"
        printf '%s\n' 'end'
        printf '%s\n' 'local ok, err = xpcall(main, debug.traceback)'
        printf '%s\n' 'if not ok then'
        printf '%s\n' '  output.writeLine(err)'
        printf '%s\n' 'end'
        printf '%s\n' 'output.close()'
        printf '%s\n' 'local status = fs.open("/headless-status", "w")'
        printf '%s\n' 'status.writeLine(ok and "OK" or "FAIL")'
        printf '%s\n' 'status.close()'
        printf '%s\n' 'os.shutdown()'
    } > "$runner"
    craftos --directory "$data_dir" --headless "${rom_arg[@]}" --mount-ro "/headless=$stage_dir" --exec "shell.run('/headless/exec.lua')" >"$tmp" 2>&1 &
    pid="$!"
    ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) &
    watchdog="$!"
    wait "$pid" >/dev/null 2>&1
    status="$?"
    kill "$watchdog" >/dev/null 2>&1 || true
    wait "$watchdog" >/dev/null 2>&1 || true
    red=$(printf '\033[31m'); reset=$(printf '\033[0m')
    if [ -f "$status_path" ] && grep -q '^OK$' "$status_path"; then
        if [ -f "$output_path" ]; then cat "$output_path"; fi
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
    else
        if [ "$status" -eq 143 ]; then
            printf '%s\n' "${red}FAIL${reset} CraftOS headless exec timed out after ${timeout_seconds}s" >&2
        else
            printf '%s\n' "${red}FAIL${reset} CraftOS headless exec failed" >&2
        fi
        if [ -f "$output_path" ]; then
            cat "$output_path" >&2
        else
            cat "$tmp" >&2
        fi
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
        exit 1
    fi

# End-to-end install probe: drive the real one-shot installer
# (install-trapos.lua, which bootstraps ccpm then runs `ccpm update` +
# `ccpm install trapos` itself) on a fresh, ephemeral CraftOS-PC state.
# Always installs from the published `master` branch (the only channel).
# Network-dependent and slower than `just test`, so not part of `just ci`.
# Override timeout with TRAPOS_INSTALL_TIMEOUT_SECONDS (default 60).
# See ADR-0005.
trapos-install: check-install
    #!/usr/bin/env bash
    set -uo pipefail
    repo='{{justfile_directory()}}'
    timeout_seconds="${TRAPOS_INSTALL_TIMEOUT_SECONDS:-60}"
    case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAPOS_INSTALL_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac
    rom_arg=()
    if [ "$(uname -s)" = "Darwin" ]; then
        rom_arg=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
    fi
    data_dir="$(mktemp -d)"
    stage_dir="$(mktemp -d)"
    cp "$repo/install-trapos.lua" "$stage_dir/install-trapos.lua"
    tmp="$(mktemp)"
    exec_code="shell.run('/staging/install-trapos'); if fs.exists('/trapos/manifest.json') and fs.exists('/trapos/ccpm.lock.json') then print('__TRAPOS_INSTALL_OK__') end; os.shutdown()"
    craftos --directory "$data_dir" --headless "${rom_arg[@]}" --mount-ro "/staging=$stage_dir" --exec "$exec_code" >"$tmp" 2>&1 &
    pid="$!"
    ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) &
    watchdog="$!"
    wait "$pid" >/dev/null 2>&1
    status="$?"
    kill "$watchdog" >/dev/null 2>&1 || true
    wait "$watchdog" >/dev/null 2>&1 || true
    red=$(printf '\033[31m'); reset=$(printf '\033[0m')
    if grep -q __TRAPOS_INSTALL_OK__ "$tmp"; then
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
        printf '%s\n' 'OK: trapos installed end-to-end on fresh CraftOS-PC'
    else
        if [ "$status" -eq 143 ]; then
            printf '%s\n' "${red}FAIL${reset} trapos-install timed out after ${timeout_seconds}s" >&2
        else
            printf '%s\n' "${red}FAIL${reset} trapos-install did not print __TRAPOS_INSTALL_OK__ (status=$status)" >&2
        fi
        cat "$tmp" >&2
        rm -f "$tmp"; rm -rf "$data_dir"; rm -rf "$stage_dir"
        exit 1
    fi

# Human-only interactive REPL. LLM agents must not execute this command.
repl:
    @just trapos --cli
