#!/usr/bin/env bash
# mosaic — fleet launcher (shipped-first, split-home safe).
#
# T110 / P5-RM-009 stack side. Carries the T106 brain launcher contract
# (shipped-first, worktree dev opt-in, OFF pass-through, typed failure) with
# one split-home correction: the SHIPPED npm mosaic is resolved from the real
# user's home (passwd database), never from $HOME. Under split-home seat
# layouts HOME is a seat home: it carries no npm prefix, and a
# $HOME/.npm-global there would be a plantable descriptor, so the $HOME
# candidate is consulted only when the passwd lookup itself fails, and then
# only with a full symlink-component refusal (secure descriptor traversal).
#
# Contract:
#   1. SHIPPED npm mosaic is the default. Candidate order:
#        a. <real-home>/.npm-global/bin/mosaic — real home from the passwd
#           database. The final component may be npm's own bin symlink into
#           lib/node_modules; that indirection is npm's layout, not a plant.
#        b. $HOME/.npm-global/bin/mosaic — ONLY when the passwd lookup
#           fails. Untrusted: every path component must be a non-symlink or
#           the candidate is refused without execution.
#   2. Worktree build is DEV OPT-IN: used only when MOSAIC_CLI_WORKTREE is
#      explicitly set. Health-checked via --version; ANY doubt (absent,
#      unreadable, or failing) falls back to the shipped npm mosaic with a
#      warning on stderr. With no environment set, worktree candidates are
#      never consulted — stale worktree builds cannot regain precedence.
#   3. MOSAIC_FLEET_CLI_OFF keeps its pass-through semantics: set (any
#      value) forces pure pass-through. The dev path is not consulted even
#      when MOSAIC_CLI_WORKTREE is also set.
#   4. Typed failure: with no runnable candidate the launcher prints one
#      stderr line naming what was checked and exits 127.
#   5. NEVER writes to the mosaic home or the npm prefix. Deployment to the
#      fleet goes through the real channel (PR to next -> mosaic update).
#
# Env:
#   MOSAIC_CLI_WORKTREE  dev opt-in: path to a stack worktree whose
#                        packages/mosaic/dist/cli.js is used (health-checked,
#                        shipped fallback on doubt)
#   MOSAIC_FLEET_CLI_OFF set (any value) to force pure pass-through
#
# Component walk note: the descriptor guard splits on "/" without quoting so
# multi-byte HOME paths with spaces are not supported for the FALLBACK
# candidate; the passwd candidate needs no walk (trusted derivation).

set -u

fail() {
    echo "mosaic: $*" >&2
    exit 127
}

# Real user home from the passwd database (HOME-independent).
real_home() {
    getent passwd "$(id -u)" 2>/dev/null | cut -d: -f6
}

# Refuse when any component of the candidate path is a symlink. Used only for
# the untrusted $HOME fallback candidate (contract 1b).
path_has_symlink_component() {
    local path="$1" dir base acc="" part
    dir="$(dirname -- "$path")"
    base="$(basename -- "$path")"
    local IFS='/'
    for part in $dir; do
        acc="$acc/$part"
        [ -L "$acc" ] && return 0
    done
    [ -L "$dir/$base" ] && return 0
    return 1
}

# Print shipped candidates in contract order. Refusals are reported on stderr
# so the typed failure names the cause.
shipped_candidates() {
    local rh home_candidate
    rh="$(real_home)"
    if [ -n "$rh" ]; then
        printf '%s\n' "$rh/.npm-global/bin/mosaic"
        return 0
    fi
    # passwd lookup failed: the only fallback is $HOME, descriptor-guarded.
    if [ -n "${HOME:-}" ]; then
        home_candidate="$HOME/.npm-global/bin/mosaic"
        if path_has_symlink_component "$home_candidate"; then
            echo "mosaic: refusing \$HOME candidate $home_candidate: symlink component (untrusted without a passwd home)" >&2
        else
            printf '%s\n' "$home_candidate"
        fi
    fi
    return 0
}

resolve_shipped() {
    local candidate
    while IFS= read -r candidate; do
        [ -n "$candidate" ] || continue
        if [ -x "$candidate" ]; then
            printf '%s\n' "$candidate"
            return 0
        fi
    done < <(shipped_candidates)
    return 1
}

# Dev opt-in only: an explicit MOSAIC_CLI_WORKTREE reaches the worktree build,
# and pure pass-through (MOSAIC_FLEET_CLI_OFF) outranks it.
if [ -n "${MOSAIC_CLI_WORKTREE:-}" ] && [ -z "${MOSAIC_FLEET_CLI_OFF:-}" ]; then
    CLI="$MOSAIC_CLI_WORKTREE/packages/mosaic/dist/cli.js"
    if [ -r "$CLI" ]; then
        if v="$(node "$CLI" --version 2>/dev/null)" && [ -n "$v" ]; then
            exec node "$CLI" "$@"
        fi
        echo "mosaic: worktree build at $CLI failed its health check; using shipped npm mosaic" >&2
    else
        echo "mosaic: worktree build at $CLI absent or unreadable; using shipped npm mosaic" >&2
    fi
fi

SHIPPED="$(resolve_shipped)" || true
if [ -n "${SHIPPED:-}" ]; then
    exec "$SHIPPED" "$@"
fi

fail "no runnable CLI (shipped npm mosaic absent from the passwd-home npm prefix and \$HOME; worktree build requires MOSAIC_CLI_WORKTREE)"
