#!/usr/bin/env bash
# Launcher for the Godot port agent.
#
#   ./sylph-port build          build the image
#   ./sylph-port shell          interactive shell
#   ./sylph-port loose [task]   detached, self-running on a fixed interval
#   ./sylph-port logs -f        follow it
#   ./sylph-port attach         chat with it (Ctrl-P Ctrl-Q to leave it running)
#   ./sylph-port remote         a link to chat with it from anywhere
#   ./sylph-port stop           stop it
#
# Env:
#   SYLPH_PORT_CPUS / SYLPH_PORT_MEM_GB   override the cap (default 3 / 4)
#   SYLPH_REBORN        path to the Syplheed-Reborn checkout (read-only mount)
#   SYLPH_DISC          extracted disc root
#   SYLPH_GIT_CREDENTIALS   file with `https://<user>:<token>@host` for push-work
#   SYLPH_LOOP_INTERVAL     fixed loop cadence (default 45m)
#
# ── Two hard-won constraints ────────────────────────────────────────────────
#
# 1. THIS REPO IS ITS OWN CLONE. It is deliberately NOT the tree the RE agent
#    or a human is working in. Sharing a working tree between two writers means
#    files change under whoever is mid-edit, and a `git add -A` by one sweeps up
#    the other's work. That happened; do not re-create it.
#
# 2. IDENTITY GOES IN THE ENVIRONMENT, NOT `.git/config`. Writing `[user]` into
#    a repo's config captures every commit made in that tree, including a
#    human's. GIT_AUTHOR_*/GIT_COMMITTER_* apply to this container's commits and
#    nobody else's.
set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/.." && pwd)"
IMAGE="${SYLPH_PORT_IMAGE:-sylpheed-port:latest}"
NAME="${SYLPH_PORT_NAME:-sylpheed-port}"

# Half of what the RE container takes. That container builds a C++ emulator and
# drives it; this one converts assets and runs Godot. Two full-size containers
# do not fit on a 12-core / 15 GB box beside a desktop -- memory is the binding
# constraint, and an over-committed build has crashed this machine before.
CPUS="${SYLPH_PORT_CPUS:-3}"
MEM_GB="${SYLPH_PORT_MEM_GB:-4}"

REBORN="${SYLPH_REBORN:-$(cd "$REPO/../Syplheed-Reborn" 2>/dev/null && pwd || true)}"
DISC="${SYLPH_DISC:-$(cd "$REPO/../sylph_extract" 2>/dev/null && pwd || true)}"

docker_args() {
  local _out=(
    --name "$NAME"
    --hostname sylph-port
    --cpus "$CPUS"
    --memory "${MEM_GB}g"
    --memory-swap "${MEM_GB}g"     # no swap escape hatch: a swapping build
                                   # thrashes the whole host
    --pids-limit 2048
    -v "$REPO:/work"
    -v "sylpheed-port-target:/sylph-home/port/target-container"
    -v "sylpheed-port-claude:/sylph-home/port/.claude"
    -e "PROJECT_DIR=/work"
  )

  # The RE corpus, READ-ONLY. `docs/port/HANDOFF.md` is the contract, and the
  # agent also builds sylpheed-cli from here for the reference renderer. Mounted
  # ro so a port iteration cannot edit the other agent's repository.
  if [ -n "$REBORN" ] && [ -d "$REBORN" ]; then
    _out+=(-v "$REBORN:/reborn:ro")
  else
    echo "==> NOTE: no Syplheed-Reborn checkout found; the agent cannot read" >&2
    echo "    HANDOFF.md or build the reference renderer. Set SYLPH_REBORN." >&2
  fi

  if [ -n "$DISC" ] && [ -d "$DISC" ]; then
    _out+=(-v "$DISC:/disc:ro" -e "SYLPHEED_DISC=/disc")
  else
    echo "==> NOTE: no extracted disc found; the exporter has nothing to read." >&2
    echo "    Set SYLPH_DISC to the directory holding dat/ and hidden/." >&2
  fi

  # Commits are attributed to the port agent, via the environment so that
  # nothing is written into the repository's config. See constraint 2 above.
  _out+=(
    -e "GIT_AUTHOR_NAME=Sylpheed port agent"
    -e "GIT_AUTHOR_EMAIL=port-agent@localhost"
    -e "GIT_COMMITTER_NAME=Sylpheed port agent"
    -e "GIT_COMMITTER_EMAIL=port-agent@localhost"
  )

  local gitcred="${SYLPH_GIT_CREDENTIALS:-$HOME/.sylph-git-credentials}"
  if [ -f "$gitcred" ]; then
    _out+=(-v "$gitcred:/sylph-home/port/.git-credentials:ro")
  else
    echo "==> NOTE: no git credentials at $gitcred — the agent cannot push," >&2
    echo "    so its work dies with the container." >&2
  fi

  [ -n "${ANTHROPIC_API_KEY:-}" ] && _out+=(-e "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY")
  printf '%s\n' "${_out[@]}"
}

mapfile -t ARGS < <(docker_args)

case "${1:-}" in
  build)
    exec docker build -t "$IMAGE" \
      --build-arg "AGENT_UID=$(id -u)" --build-arg "AGENT_GID=$(id -g)" "$HERE"
    ;;

  shell)
    TTY=(-i); [ -t 0 ] && TTY=(-it)
    exec docker run --rm "${TTY[@]}" "${ARGS[@]}" "$IMAGE" bash
    ;;

  loose)
    shift
    TASK="${1:-}"
    if [ -z "$TASK" ]; then
      if [ -f "$REPO/docs/loop-task.md" ]; then
        TASK="$(cat "$REPO/docs/loop-task.md")"
      else
        TASK="Work the milestones in docs/MISSION.md."
      fi
    fi
    # A FIXED interval, not self-pacing: the one thing an agent deep in a
    # milestone reliably forgets is the bookkeeping after it, and a forgotten
    # wake-up silently ends the loop.
    INTERVAL="${SYLPH_LOOP_INTERVAL-45m}"
    echo "==> loose  |  cpus=$CPUS  mem=${MEM_GB}g  pacing=${INTERVAL:-self}"
    echo "==> repo:   $REPO"
    echo "==> reborn: ${REBORN:-<none>} (read-only)"
    docker run -d -i -t "${ARGS[@]}" "$IMAGE" \
      "/loop ${INTERVAL:+$INTERVAL }$TASK" >/dev/null
    echo
    echo "    running detached as '$NAME'."
    echo "      ./sylph-port remote      link to chat with it from anywhere"
    echo "      ./sylph-port logs -f     follow it"
    echo "      ./sylph-port attach      chat with it locally"
    echo "      ./sylph-port stop        stop it"
    ;;

  logs)   shift; exec docker logs "$@" "$NAME" ;;
  attach) exec docker attach "$NAME" ;;
  stop)   exec docker rm -f "$NAME" ;;

  remote)
    echo "waiting for the session to register" >&2
    for _ in $(seq 60); do
      url=$(docker exec "$NAME" sh -c \
        'grep -ho "https://claude.ai/code/session_[A-Za-z0-9]*" \
           /sylph-home/port/.claude/**/*.jsonl 2>/dev/null | tail -1' 2>/dev/null || true)
      [ -n "$url" ] && { echo "$url"; exit 0; }
      sleep 2
    done
    echo "no session link yet — try ./sylph-port logs -f" >&2
    exit 1
    ;;

  *)
    sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
    ;;
esac
