#!/bin/bash

project_dir=$( pwd -P ) || exit 1
home_dir=$( readlink -e -- "${HOME}" ) || exit 1

if ! bubblewrap=$( command -v bwrap ); then
  echo "bubblewrap (bwrap) is required" >&2
  exit 1
fi
agents=(
  $( command -v claude )
  $( command -v codex )
  $( command -v pi )
  $( command -v ps ) # for testing
  $( command -v ls ) # for testing
  $( command -v touch ) # for testing
  $( command -v bash ) # for testing
)

if [[ "${project_dir}" = "${home_dir}" ]]; then
     echo "Not running in ${HOME}" >&2
     exit 1
fi

if [[ -n "${1}" ]] && [[ -n $( command -v "${1}" ) ]]; then
	coder=$( command -v "${1}" ) && shift
else
	echo "Please specify a coding agent" >&2
	exit 1
fi

if ! [[ " ${agents[@]} " =~ " ${coder} " ]]; then
	echo "No agent found" >&2
	exit 1
fi

declare -a ro_binds
declare -a rw_binds
declare -a bwrap_args
declare -a bind_sources
declare -a bind_destinations

ro_binds=(
  /usr
  /lib
  /lib64
  /bin
  /etc/resolv.conf
  /etc/hosts
  /etc/ssl
  /etc/passwd
  /etc/group
  "${HOME}/sources"
)

for readonly_path in "${HOME}/.gitconfig" "${HOME}/bin" "${HOME}/.local" "${HOME}/.atuin" "${XDG_RUNTIME_DIR}"; do
  if [[ -e "${readonly_path}" ]]; then
    ro_binds+=("${readonly_path}")
  fi
done

for readwrite_path in "${HOME}/.claude" "${HOME}/.claude.json" "${HOME}/.local/share/claude" "${HOME}/.pi" "${project_dir}"; do
  if [[ -e "${readwrite_path}" ]]; then
    rw_binds+=("${readwrite_path}")
  fi
done

# Resolve mount sources, but keep the paths applications expect as destinations.
for readonly_path in "${ro_binds[@]}"; do
  bind_source=$( readlink -e -- "${readonly_path}" ) || exit 1
  bwrap_args+=("--ro-bind" "${bind_source}" "${readonly_path}")
  bind_sources+=("${bind_source}")
  bind_destinations+=("${readonly_path}")
done
for readwrite_path in "${rw_binds[@]}"; do
  bind_source=$( readlink -e -- "${readwrite_path}" ) || exit 1
  bwrap_args+=("--bind" "${bind_source}" "${readwrite_path}")
  bind_sources+=("${bind_source}")
  bind_destinations+=("${readwrite_path}")
done

git_config="${project_dir}/.git/config"
git_worktree_config="${project_dir}/.git/config.worktree"
if [[ -f "${project_dir}/.git" ]]; then
  # Linked worktrees keep their shared and per-worktree config elsewhere.
  git_config=$( git -C "${project_dir}" rev-parse --path-format=absolute --git-path config ) || exit 1
  git_worktree_config=$( git -C "${project_dir}" rev-parse --path-format=absolute --git-path config.worktree ) || exit 1
fi

# Mask secrets after all directory mounts so later binds cannot expose them.
for hide_path in "${git_config}" "${git_worktree_config}" "${project_dir}/.env"; do
  if [[ -e "${hide_path}" ]]; then
    hide_path=$( readlink -e -- "${hide_path}" ) || exit 1
    bwrap_args+=("--ro-bind" /dev/null "${hide_path}")
    # A symlinked mount can expose the same file at another destination.
    for i in "${!bind_sources[@]}"; do
      if [[ "${hide_path}" = "${bind_sources[${i}]}" || "${hide_path}" = "${bind_sources[${i}]}/"* ]]; then
        mask_path="${bind_destinations[${i}]}${hide_path#"${bind_sources[${i}]}"}"
        if [[ "${mask_path}" != "${hide_path}" ]]; then
          bwrap_args+=("--ro-bind" /dev/null "${mask_path}")
        fi
      fi
    done
  fi
done

"${bubblewrap}" \
     "${bwrap_args[@]}" \
     --tmpfs /tmp \
     --proc /proc \
     --dev /dev \
     --share-net \
     --unshare-pid \
     --die-with-parent \
     --chdir "${project_dir}" \
     "${coder}" "${@}"
