#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")"
REPO_DIR="$(pwd)"

# Run `./claudaris config` to set your own image/container name (so multiple
# people can each run their own container from this repo) and other options.
# It writes them to .env, which is gitignored and sourced here if present.
NAME=claudaris
CLAUDARIS_IMAGE=4lights/claudaris:latest
[ -f .env ] && set -a && . .env && set +a

# docker-compose.yml reads NAME/DATA_DIR/WORKSPACE_DIR for variable
# substitution, so every subcommand that shells out to `docker compose`
# needs them exported, with the same defaults `cmd_start` seeds on disk.
export NAME
export CLAUDARIS_IMAGE
export DATA_DIR="${DATA_DIR:-/data/$NAME}"
export WORKSPACE_DIR="${WORKSPACE_DIR:-/home/${USER:-$(id -un)}/projects}"

usage() {
  cat <<EOF
Usage: ./claudaris <command>

Commands:
  config, configure  Interactive wizard to write .env with your settings
  build              Build the container image (always --no-cache --pull)
  start              Start (or create) the container
  connect            Start the container if needed, then attach a bash shell
  remove, stop, rm   Stop and remove the container
  help               Show this message
EOF
}

# Ensure a path exists as a directory.
#
# This deliberately fails if the path already exists as a file. We don't want
# to silently replace a file with a directory because Docker bind mounts are
# sensitive to source/target types.
ensure_dir() {
  local path="$1"

  if [ -e "$path" ] && [ ! -d "$path" ]; then
    echo "ERROR: expected directory, but found a non-directory:" >&2
    echo "  $path" >&2
    exit 1
  fi

  mkdir -p "$path"
}

# Ensure a path exists as a regular file.
#
# This deliberately fails if the path already exists as a directory. Docker
# will otherwise produce the confusing:
#
#   not a directory: Are you trying to mount a directory onto a file
#
ensure_file() {
  local path="$1"

  if [ -e "$path" ] && [ ! -f "$path" ]; then
    echo "ERROR: expected regular file, but found a directory or other type:" >&2
    echo "  $path" >&2
    echo >&2
    echo "Remove or rename the incorrect path, then run ./claudaris start again." >&2
    exit 1
  fi

  if [ ! -e "$path" ]; then
    touch "$path"
  fi
}

cmd_config() {
  echo "claudaris config — press enter to keep the current/default value."
  echo

  read -r -p "NAME [$NAME]: " input
  NAME="${input:-$NAME}"

  local data_dir_default="${DATA_DIR:-/data/$NAME}"
  read -r -p "DATA_DIR [$data_dir_default]: " input
  DATA_DIR="${input:-$data_dir_default}"

  local workspace_dir_default="${WORKSPACE_DIR:-/home/${USER:-$(id -un)}/projects}"
  read -r -p "WORKSPACE_DIR [$workspace_dir_default]: " input
  WORKSPACE_DIR="${input:-$workspace_dir_default}"

  local claudaris_image_default="${CLAUDARIS_IMAGE:-4lights/claudaris:latest}"
  read -r -p "CLAUDARIS_IMAGE [$claudaris_image_default]: " input
  CLAUDARIS_IMAGE="${input:-$claudaris_image_default}"

  cat > .env <<EOF
# Written by \`./claudaris config\` — gitignored, per-user.
NAME=$NAME
CLAUDARIS_IMAGE=$CLAUDARIS_IMAGE
DATA_DIR=$DATA_DIR
WORKSPACE_DIR=$WORKSPACE_DIR
EOF

  echo
  echo "Wrote .env:"
  cat .env
}

cmd_build() {
  export BUILDX_NO_DEFAULT_ATTESTATIONS=1
  docker compose build --no-cache --pull
}

cmd_start() {
  echo "Preparing persistent directories and files under:"
  echo "  $DATA_DIR"
  echo

  # ---------------------------------------------------------------------------
  # Home / Bash
  # ---------------------------------------------------------------------------

  # /data/claudaris/home must be a directory.
  ensure_dir "$DATA_DIR/home"

  # .bashrc is bind-mounted to /root/.bashrc, so it MUST be a file.
  if [ ! -e "$DATA_DIR/home/.bashrc" ]; then
    cp "$REPO_DIR/files/bashrc" "$DATA_DIR/home/.bashrc"
  elif [ ! -f "$DATA_DIR/home/.bashrc" ]; then
    echo "ERROR: $DATA_DIR/home/.bashrc exists but is not a regular file." >&2
    echo "Please remove or rename it, then run ./claudaris start again." >&2
    exit 1
  fi

  # bash_aliases is bind-mounted to /opt/dotfiles/bash_aliases, which is a
# regular file in the image, so the host source MUST also be a regular file.
if [ ! -e "$DATA_DIR/home/bash_aliases" ]; then
  cp "$REPO_DIR/files/bash_aliases" "$DATA_DIR/home/bash_aliases"
elif [ ! -f "$DATA_DIR/home/bash_aliases" ]; then
  echo "ERROR: $DATA_DIR/home/bash_aliases exists but is not a regular file." >&2
  echo "Please remove or rename it, then run ./claudaris start again." >&2
  exit 1
fi

  # ---------------------------------------------------------------------------
  # Claude Code
  # ---------------------------------------------------------------------------

  ensure_dir "$DATA_DIR/claude"

  # Both of these are individual file bind mounts. They MUST be files.
  # Empty files are intentional on first run; Claude Code will populate them.
  ensure_file "$DATA_DIR/claude/credentials.json"
  ensure_file "$DATA_DIR/claude/claude.json"

  # ---------------------------------------------------------------------------
  # OpenCode
  # ---------------------------------------------------------------------------

  ensure_dir "$DATA_DIR/opencode"

  # Individual file bind mount; MUST be a file.
  ensure_file "$DATA_DIR/opencode/auth.json"

  # ---------------------------------------------------------------------------
  # Optional SSH mount
  # ---------------------------------------------------------------------------

  # Optional: mount ~/.ssh into the container (read-only) for connecting out to
  # other nodes. Opt in by creating $DATA_DIR/ssh and populating it with
  # keys/config before starting the container.
  #
  # docker-compose.yml doesn't list this mount because Compose can't make a
  # volume conditional on a host path existing. Instead, create a gitignored
  # override file that Compose picks up automatically.
  if [ -d "$DATA_DIR/ssh" ]; then
    cat > "$REPO_DIR/docker-compose.override.yml" <<EOF
services:
  claudaris:
    volumes:
      - $DATA_DIR/ssh:/root/.ssh:ro
EOF
  elif [ -e "$DATA_DIR/ssh" ]; then
    echo "ERROR: $DATA_DIR/ssh exists but is not a directory." >&2
    echo "Please remove or rename it, then run ./claudaris start again." >&2
    exit 1
  else
    rm -f "$REPO_DIR/docker-compose.override.yml"
  fi

  # ---------------------------------------------------------------------------
  # Show mount sources before Docker starts.
  # ---------------------------------------------------------------------------

  echo "Persistent paths:"
  printf '  %-15s %s\n' "home/" "$DATA_DIR/home/"
  printf '  %-15s %s\n' ".bashrc" "$DATA_DIR/home/.bashrc"
  printf '  %-15s %s\n' "bash_aliases/" "$DATA_DIR/home/bash_aliases/"
  printf '  %-15s %s\n' "credentials" "$DATA_DIR/claude/credentials.json"
  printf '  %-15s %s\n' "claude.json" "$DATA_DIR/claude/claude.json"
  printf '  %-15s %s\n' "auth.json" "$DATA_DIR/opencode/auth.json"

  if [ -d "$DATA_DIR/ssh" ]; then
    printf '  %-15s %s\n' "ssh/" "$DATA_DIR/ssh/"
  fi

  echo
  echo "Starting container..."

  docker compose up -d
}

cmd_connect() {
  # The container exits on its own once the main tmux session ends (see
  # entrypoint.sh) and isn't auto-restarted, so make sure it's up before
  # exec'ing in — a no-op if it's already running. Plain bash rather than
  # tmux here: when this container runs on a remote host, the caller is
  # typically already inside a tmux session locally, and nesting tmux in
  # tmux is annoying. tmux itself stays installed and available — run
  # `tmux` manually inside the container if you want it.
  docker compose up -d >/dev/null
  docker compose exec claudaris bash
}

cmd_remove() {
  docker compose down
}

case "${1:-help}" in
  config|configure) cmd_config ;;
  build) cmd_build ;;
  start) cmd_start ;;
  connect) cmd_connect ;;
  remove|stop|rm) cmd_remove ;;
  help|-h|--help) usage ;;
  *)
    echo "Unknown command: $1" >&2
    usage >&2
    exit 1
    ;;
esac