#!/usr/bin/env bash
#
# bwrap-opencode - Run Opencode inside a bubblewrap sandbox for ow development.
#
# Drop-in wrapper for the `opencode` CLI. Provides filesystem isolation by
# running Opencode inside a bubblewrap (bwrap) container. Only explicitly
# allowed directories are visible to Opencode inside the sandbox.
#
# This script is for developing on the ow project itself. For Odoo workspaces,
# use the bwrap-opencode script generated in the workspace directory.
#
# REQUIREMENTS
#   - bubblewrap (bwrap) installed and available on PATH
#   - Opencode binary at ~/.opencode/bin/opencode
#
#   Optional (mounts use bind-try and are silently skipped if absent):
#   - Google Chrome or Chromium (/opt/google/chrome or /snap/chromium)
#   - PostgreSQL (Unix socket at /var/run/postgresql)
#   - Odoo filestore (~/.local/share/Odoo)
#
# USAGE
#   bwrap-opencode [--add-dir <path> ...] [opencode args...]
#
# OPTIONS
#   --add-dir <path>
#       Bind-mount <path> read-write into the sandbox in addition to the
#       pre-configured ow directory. Opencode does not support --add-dir
#       natively, so these are only used for bwrap bind-mounts. All other
#       arguments are forwarded to opencode.
#
# SANDBOX LAYOUT
#   Read-write mounts:
#     ~/.cache/opencode          Opencode cache
#     ~/.config/opencode         Opencode configuration
#     ~/.local/share/opencode    Opencode data
#     ~/.opencode                Opencode binary
#     $OW_ROOT                   ow project directory
#     <--add-dir paths>          Extra directories passed by the caller
#
#   Optional read-write mounts (bind-try, skipped if absent):
#     ~/.local/share/Odoo        Odoo filestore
#     /var/run/postgresql        PostgreSQL Unix socket (local DB access)
#
#   Read-only mounts:
#     /usr (+ /bin, /sbin, /lib, /lib64 symlinks)
#                                System binaries and libraries
#     /etc/alternatives          Symlink targets (Chrome binary chain)
#     /etc/hosts                 Hostname resolution
#     /etc/passwd                User database (required for PostgreSQL auth)
#     /etc/ssl                   TLS certificates
#     /run/systemd/resolve       Systemd-resolved DNS
#
#   Optional read-only mounts (bind-try, skipped if absent):
#     /opt/google/chrome         Chrome installation
#     /snap/bin, /snap/chromium  Snap Chromium installation
#     /etc/fonts                 Font configuration
#     ~/.fonts, ~/.local/share/fonts  User fonts
#
#   Ephemeral:
#     /dev                       Device nodes (via --dev)
#     /proc                      Process info (via --proc)
#     /tmp                       Temporary files (tmpfs, cleared on exit)
#
# NAMESPACE ISOLATION
#   Unshared: IPC, UTS, cgroup, PID
#   Shared:
#     - Network: Opencode requires outbound internet access.
#
# WORKING DIRECTORY
#   The sandbox starts in $OW_ROOT (the ow project directory).
#
set -e

HOME="${HOME:-/home/$(whoami)}"
OPENCODE_BIN="${OPENCODE_BIN:-$HOME/.opencode/bin/opencode}"

if [[ ! -x "$OPENCODE_BIN" ]]; then
  echo "Opencode not found at $OPENCODE_BIN" >&2
  exit 1
fi

OPENCODE_DIRS=(
  "$HOME/.cache/opencode"
  "$HOME/.config/opencode"
  "$HOME/.local/share/opencode"
  "$HOME/.opencode"
)
for d in "${OPENCODE_DIRS[@]}"; do
  mkdir -p "$d"
done

OW_ROOT="$(cd "$(dirname "$0")" && pwd)"

ALLOW_DIRS=("$OW_ROOT")
OPENCODE_ARGS=()
while [[ $# -gt 0 ]]; do
  if [[ "$1" == "--add-dir" ]]; then
    shift
    ALLOW_DIRS+=("$(cd "$1" && pwd)")
    shift
  else
    OPENCODE_ARGS+=("$1")
    shift
  fi
done

BWRAP=(
  --ro-bind /run/systemd/resolve /run/systemd/resolve
  --ro-bind /etc/hosts /etc/hosts
  --ro-bind /etc/ssl /etc/ssl
  --ro-bind-try /etc/ca-certificates /etc/ca-certificates
  --ro-bind-try /usr/share/ca-certificates /usr/share/ca-certificates
  --symlink ../run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
  --ro-bind /usr /usr
  --symlink usr/bin   /bin
  --symlink usr/sbin  /sbin
  --symlink usr/lib   /lib
  --symlink usr/lib64 /lib64
  --dev /dev
  --proc /proc
  --tmpfs /tmp
  --chdir "$OW_ROOT"
  --unshare-all
  --share-net
  --die-with-parent
  --new-session
  --bind-try "$HOME/.local/share/Odoo" "$HOME/.local/share/Odoo"
  --bind-try /var/run/postgresql /var/run/postgresql
  --ro-bind /etc/passwd /etc/passwd
  --ro-bind-try /etc/alternatives /etc/alternatives
  --ro-bind-try /opt/google/chrome /opt/google/chrome
  --ro-bind-try /snap/bin /snap/bin
  --ro-bind-try /snap/chromium /snap/chromium
  --ro-bind-try /etc/fonts /etc/fonts
  --ro-bind-try "$HOME/.fonts" "$HOME/.fonts"
  --ro-bind-try "$HOME/.local/share/fonts" "$HOME/.local/share/fonts"
)

for path in "${OPENCODE_DIRS[@]}"; do
  BWRAP+=(--bind "$path" "$path")
done

for path in "${ALLOW_DIRS[@]}"; do
  BWRAP+=(--bind "$path" "$path")
done

echo "Running: bwrap ${BWRAP[@]} -- $OPENCODE_BIN ${OPENCODE_ARGS[@]}" >&2
exec bwrap "${BWRAP[@]}" -- "$OPENCODE_BIN" "${OPENCODE_ARGS[@]}"
