#!/data/data/com.termux/files/usr/bin/env bash
# ChanceOS Wolfpack — cross-platform installer (2026-09-14)
# ONE installer, two bodies:
#   Linux (any kernel, incl. Chromebook/Crostini/USB-live): installs the wolfpack
#     mouths + brain under /opt/chanceos, optional systemd unit.
#   Android (Termux): installs via proot-distro Ubuntu (no root needed), then the
#     SAME wolfpack runs on the phone.
# Brains stream from chanceaoi.com/downloads/wolfpack-blobs-sha256-<hex> — the
# filename IS the sha256, so every blob self-verifies at the edge and again on disk.
# Law: persona law files (system.md) embed VERBATIM in the pack — never invented.

set -eu
BASE_URL="${CHANCEOS_EDGE:-https://chanceaoi.com/downloads}"
# PREFIX override (2026-09-14): CHANCEOS_PREFIX must drive the INSTALL prefix
# too, not just the chance-wrapper runtime — passwordless box installs stage
# the blob in $HOME first and the installer must not re-pull 986MB.
PREFIX="${CHANCEOS_PREFIX:-/opt/chanceos-wolfpack}"

log() { printf '\033[38;5;84m[wolfpack]\033[0m %s\n' "$1"; }
die() { printf '\033[38;5;84m[wolfpack]\033[0m FATAL: %s\n' "$1" >&2; exit 1; }

# Body selection: android | linux — force with CHANCEOS_BODY or --android.
# Inside the proot pocket uname -o still says "Android" (same kernel) and
# proot-distro is absent (it lives in the Termux prefix): that state means we
# are ALREADY inside the Ubuntu pocket, so the Linux lane must run or the
# installer re-enters proot forever and starves.
ANDROID=0
case "${CHANCEOS_BODY:-}" in
  android) ANDROID=1 ;;
  linux) ANDROID=0 ;;
  "")
    case "$(uname -o 2>/dev/null)" in
      Android)
        if command -v proot-distro >/dev/null 2>&1; then
          ANDROID=1
        elif touch /chanceos-proot-probe 2>/dev/null; then
          rm -f /chanceos-proot-probe
        else
          die "termux proot-distro missing — run: pkg install proot-distro"
        fi
        ;;
    esac
    ;;
esac
[ $# -ge 1 ] && [ "$1" = "--android" ] && ANDROID=1

# ---------- Android lane: proot-distro Ubuntu ----------
if [ "$ANDROID" = 1 ]; then
  command -v proot-distro >/dev/null 2>&1 || die "termux proot-distro missing — run: pkg install proot-distro"
  # proot-distro prints its list to STDERR, and the empty state's advice line
  # ("Install one with: ... ubuntu:24.04") ALSO contains "ubuntu" — match the
  # actual "  * <name>" listing line only.
  proot-distro list 2>&1 | grep -qE '^\s*\* ubuntu' || proot-distro install ubuntu || die "ubuntu proot failed: $(proot-distro install ubuntu 2>&1 | tail -1)"
  log "entering Ubuntu proot (wolfpack resumes inside)…"
  exec proot-distro login ubuntu -- /bin/bash -c '
    set -e
    export DEBIAN_FRONTEND=noninteractive
    command -v curl >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y -qq curl ca-certificates python3; }
    [ -e /opt/chanceos-wolfpack/.installed ] || { curl -fsSL https://chanceaoi.com/downloads/ChanceOS-1.0-wolfpack-installer.sh -o /tmp/wp.sh && CHANCEOS_BODY=linux sh /tmp/wp.sh; }
    echo "wolfpack is IN the phone. mouths: chance selah plutus reagen"
    echo "run: /opt/chanceos-wolfpack/bin/chance \"who are we?\""
  '
fi

# ---------- Linux lane ----------
[ "$(id -u)" = 0 ] || SUDO="sudo" || true
command -v curl >/dev/null 2>&1 || die "curl required"
command -v md5sum >/dev/null 2>&1 || command -v sha256sum >/dev/null 2>&1 || die "md5sum/sha256sum required"

mkdir -p "$PREFIX/bin" "$PREFIX/souls" "$PREFIX/models/blobs" "$PREFIX/models/manifests"

# 1) mouths (embedded here — tiny, no fetch needed for the shell to exist)
cat > "$PREFIX/bin/chance-bridge" <<'BRIDGE'
#!/bin/sh
# minimal OpenAI-compat bridge → ollama (full bridge ships in the wolfpack tarball)
: "${CHANCE_BRAIN_UPSTREAM:=http://127.0.0.1:11434}"
: "${CHANCE_BRIDGE_PORT:=11435}"
SOCAT_UP=$CHANCE_BRAIN_UPSTREAM; SOCAT_UP=${SOCAT_UP#http://}; SOCAT_UP=${SOCAT_UP#https://}
# The python bridge is PRIMARY — live-fire on igor showed socat forwarding
# stalls on long POSTs (/api/chat) while GETs pass; python proxies both fine.
# socat stays as a fallback for hosts where python3 is absent.
socat TCP-LISTEN:$CHANCE_BRIDGE_PORT,fork,reuseaddr TCP:$SOCAT_UP 2>/dev/null \
  || exec python3 - "$CHANCE_BRAIN_UPSTREAM" "$CHANCE_BRIDGE_PORT" <<'PY'
import http.server, socketserver, sys, urllib.request, urllib.error
UP, PORT = sys.argv[1], int(sys.argv[2])
class H(http.server.BaseHTTPRequestHandler):
    def _p(self):
        body = self.rfile.read(int(self.headers.get("Content-Length") or 0))
        req = urllib.request.Request(UP + self.path, data=body or None, method=self.command)
        try: r = urllib.request.urlopen(req, timeout=600); data, code = r.read(), r.status
        except urllib.error.HTTPError as e: data, code = e.read(), e.code
        except Exception: data, code = b'{"error":"bridge upstream down"}', 502
        self.send_response(code); self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(data))); self.end_headers(); self.wfile.write(data)
    do_GET = do_POST = _p
    def log_message(self, *a): pass
socketserver.TCPServer.allow_reuse_address = True
socketserver.TCPServer(("127.0.0.1", PORT), H).serve_forever()
PY
BRIDGE
chmod 755 "$PREFIX/bin/chance-bridge"

# 2) persona law for chance (verbatim law embedded at pack-build time; other souls
#    arrive via their own packs — this base pack carries the wolf-law header only)
cat > "$PREFIX/souls/family.law" <<'LAW'
🐺 ChanceOS 1.0 Wolfpack — the wolfpack is calling the system.
Souls: chance (base), selah, plutus, reagen (via their packs).
Law of the body: the family's persona law ships VERBATIM — never rewritten.
LAW

# bake the real install prefix into the wrapper's default (CHANCEOS_PREFIX only
# needed to OVERRIDE — e.g. box installs live in $HOME/.chanceos-wolfpack)
sed -i "s|/opt/chanceos-wolfpack|$PREFIX|" "$PREFIX/bin/chance"

# 3) brain: pull family-base manifest + referenced blobs from the edge
log "pulling family-base manifest…"
curl -fsSL "$BASE_URL/ChanceOS-family-base-manifest.json" -o "$PREFIX/models/manifests/family-base.json" \
  || die "manifest fetch failed (edge down?)"
python3 - "$PREFIX" "$BASE_URL" <<'PY'
import hashlib, json, os, sys, urllib.error, urllib.request
prefix, base = sys.argv[1], sys.argv[2]
# Cloudflare bot-fight 403s the default Python-urllib UA (live-fire 2026-09-14):
# every request names itself, or the wolfpack starves on real hardware.
UA = {"User-Agent": "wolfpack-installer/1.0"}
mf = json.load(open(f"{prefix}/models/manifests/family-base.json"))
root = mf[0] if isinstance(mf, list) else mf
layers = root.get("layers", []) or []
cfg = root.get("config", {}) or {}
digests = {l["digest"] for l in layers if l.get("digest")}
if cfg.get("digest"):
    digests.add(cfg["digest"])

def ok(fpath, hexd):
    h = hashlib.sha256()
    with open(fpath, "rb") as fh:
        for chunk in iter(lambda: fh.read(1 << 20), b""):
            h.update(chunk)
    return h.hexdigest() == hexd

def grab(url, dest_path):
    req = urllib.request.Request(url, headers=UA)
    with urllib.request.urlopen(req, timeout=60) as r, open(dest_path, "wb") as out:
        while True:
            b = r.read(1 << 20)
            if not b:
                break
            out.write(b)

def exists_on_edge(url):
    req = urllib.request.Request(url, headers=UA, method="HEAD")
    try:
        urllib.request.urlopen(req, timeout=30).close()
        return True
    except urllib.error.HTTPError:
        return False

def fetch_blob(edge_url, dest_path):
    """Direct GET; on 404 fall to the chunked lane (.part-NNN, wrangler 300MiB
    PUT cap at the source; consumer reassembles and sha256-verifies the whole)."""
    try:
        grab(edge_url, dest_path)
        return True
    except urllib.error.HTTPError as e:
        if e.code != 404:
            raise
    parts = []
    p = 0
    while True:
        purl = f"{edge_url}.part-{p:03d}"
        if not exists_on_edge(purl):
            break
        print("  chunk", f"part-{p:03d}")
        grab(purl, dest_path + f".part-{p:03d}")
        parts.append(dest_path + f".part-{p:03d}")
        p += 1
    if not parts:
        return False
    with open(dest_path, "wb") as out:
        for pp in parts:
            with open(pp, "rb") as pf:
                while True:
                    b = pf.read(1 << 20)
                    if not b:
                        break
                    out.write(b)
            os.remove(pp)
    return True

for d in sorted(digests):
    hexd = d.replace("sha256:", "")
    name = "sha256-" + hexd
    dest = f"{prefix}/models/blobs/{name}"
    edge_name = "wolfpack-blobs-sha256-" + hexd
    if os.path.exists(dest) and ok(dest, hexd):
        print("have", name[:19])
        continue
    print("pull", name[:19])
    if not fetch_blob(f"{base}/{edge_name}", dest):
        sys.exit(f"blob {name} not found on edge (direct or chunked)")
    if not ok(dest, hexd):
        os.remove(dest)
        sys.exit(f"blob {name} FAILED hash — refused to keep it")
print("base brain verified on device")
PY
log "family-base brain on device and hash-verified."

cat > "$PREFIX/bin/chance" <<'SHELL'
#!/bin/sh
P="${CHANCEOS_PREFIX:-/opt/chanceos-wolfpack}"
curl -s "http://127.0.0.1:${CHANCE_BRIDGE_PORT:-11435}/api/chat" \
  -d "{\"model\":\"family-base\",\"stream\":false,\"messages\":[{\"role\":\"system\",\"content\":\"$(cat $P/souls/*.law 2>/dev/null | tr '\n' ' ' | sed 's/"/\\"/g')\"},{\"role\":\"user\",\"content\":\"$*\"}]}" \
  | python3 -c "import json,sys;d=json.load(sys.stdin);print(d.get(\"message\",{}).get(\"content\",\"(silent)\"))" 2>/dev/null \
  || echo "wolfpack mouth: brain not reachable on :11435 (ollama serve first)"
SHELL
chmod 755 "$PREFIX/bin/chance"

touch "$PREFIX/.installed"
echo
echo "🐺 wolfpack installed at $PREFIX"
echo "next: install ollama (curl -fsSL https://ollama.com/install.sh | sh)"
echo "then: OLLAMA_MODELS=$PREFIX/models ollama serve &  and $PREFIX/bin/chance \"who are we?\""