#!/usr/bin/env bash
#
# Install + first-run end-to-end, run *inside* the container.
#
# Where `docker/verify-in-container.sh` proves the repository is healthy, this
# script proves a *fresh machine* can use it: it installs the skill into all four
# agent kinds with a throwaway HOME, then drives the real `taskctl` through its real
# `taskd` — create a project, create a card, get refused by the delivery gate,
# satisfy the gate with a report, and read the dictionaries back.
#
#   docker run --rm meerkat-taskpanel:verify bash scripts/verify/install-e2e.sh
#
# Isolation: `HOME` for the install is a fresh `mktemp -d`, and the board's data
# directory is another one — nothing here can touch `./.data`, `~/.claude`, or a
# developer's running board. `TASKD_PORT=0` lets the OS pick a port, so a
# developer's own taskd on 9527 is never in the way.
#
# Each step prints a `== ... ==` header. Any failure exits non-zero immediately;
# the throwaway directories (and the taskd the CLI started) are cleaned up on the
# way out.

set -euo pipefail

cd "$(dirname "$0")/../.."

step() {
  echo
  echo "== $* =="
}

CLI="$(pwd)/src/cli/index.mjs"

THROWAWAY_HOME="$(mktemp -d)"
DATA_DIR="$(mktemp -d)"
POINTER="$DATA_DIR/runtime.json"
REPORT_FILE="$DATA_DIR/report.json"

cleanup() {
  # Stop the taskd the CLI autostarted (if it did), then drop the throwaways.
  if [ -f "$POINTER" ]; then
    pid="$(node -e 'try{process.stdout.write(String(JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")).pid||""))}catch{}' "$POINTER" 2>/dev/null || true)"
    if [ -n "$pid" ]; then kill "$pid" 2>/dev/null || true; fi
  fi
  rm -rf "$THROWAWAY_HOME" "$DATA_DIR"
}
trap cleanup EXIT

# One identity for the whole run, used for *both* the acting agent and the card's
# assignee. The claim policy (`src/core/domain/claim.mjs`) only lets the assignee
# claim a card, so an actor that differs from `--assignee` is refused with
# `not_assignee` — which is exactly how this script used to fail. Keeping a single
# variable is what stops the two from drifting apart again.
AGENT_ID="install-e2e-bot"

# A board in the throwaway data dir, on an OS-assigned port. Autostart is left
# ON: starting taskd from the CLI *is* the path under test.
export TASKD_DATA_DIR="$DATA_DIR"
export TASKD_RUNTIME_POINTER="$POINTER"
export TASKD_PORT=0
export TASKD_HOST=127.0.0.1
export TASKCTL_AGENT="$AGENT_ID"
# Every command needs an actor kind; this script is automation, not an agent shim.
export TASKCTL_ACTOR_KIND=system
unset TASKD_NO_AUTOSTART

step "install.sh --target all --prefix <throwaway home>"
# The taskd keepalive guard is skipped: a real install loads the unit into the machine's
# service manager, and this throwaway install (and every CI/container run) must not. The
# bypass is the contract's own — see docs/scheduling.md. The guard's install behaviour is
# covered by test/install/taskd-guard.test.mjs, with the supervisor faked on PATH.
MEERKAT_TASKPANEL_SKIP_TASKD_SERVICE=1 bash install.sh --target all --prefix "$THROWAWAY_HOME"

for agent_dir in .claude .openclaw .codex .agents; do
  target="$THROWAWAY_HOME/$agent_dir/skills/meerkat/SKILL.md"
  if [ ! -f "$target" ]; then
    echo "FAIL: install did not write $target" >&2
    exit 1
  fi
  echo "  OK  $agent_dir/skills/meerkat/SKILL.md"
done

CONFIG_ROOT="$THROWAWAY_HOME/.meerkat-taskpanel"
for kind in claude codex pi; do
  target="$CONFIG_ROOT/.env.$kind"
  if [ ! -f "$target" ]; then
    echo "FAIL: install did not write $target" >&2
    exit 1
  fi
  echo "  OK  .meerkat-taskpanel/.env.$kind"
done
# Everything below runs as TASKCTL_ACTOR_KIND=system, which has no installed
# config: the board path must treat a missing .env.system as "no overlay".

step "taskctl project create"
node "$CLI" project create --id demo --name "E2E Demo"

step "taskctl issue create"
# `optional` is the default project policy, and this actor is non-human, so the
# review decision is explicit. `no` keeps the card on the delivery-gate path this
# script is about; a `yes` here would dispatch a review card first.
CREATE_JSON="$(node "$CLI" --json issue create \
  --project demo \
  --title "Install e2e card" \
  --acceptance-mode off \
  --acceptance "the delivery gate refuses an empty report" \
  --label e2e \
  --assignee "$AGENT_ID")"
echo "$CREATE_JSON"
ID="$(printf '%s' "$CREATE_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).data.task.identifier))')"
echo "  task identifier: $ID"

step "taskctl issue move $ID in_progress"
node "$CLI" issue move "$ID" in_progress

step "taskctl issue move $ID in_review  (NEGATIVE: no report — must be refused)"
set +e
NEGATIVE="$(node "$CLI" --json issue move "$ID" in_review 2>&1)"
STATUS=$?
set -e
echo "$NEGATIVE"
if [ "$STATUS" -eq 0 ]; then
  echo "FAIL: moving to in_review without a report succeeded (exit 0); the gate is open" >&2
  exit 1
fi
case "$NEGATIVE" in
  *REPORT_REQUIRED*) ;;
  *)
    echo "FAIL: expected the refusal to name REPORT_REQUIRED (exit was $STATUS)" >&2
    exit 1
    ;;
esac
echo "  OK  refused with exit $STATUS and error code REPORT_REQUIRED"

step "taskctl report template $ID > report.json"
# A command anchor, not the default placeholder: the verify image has no git, so the
# template cannot stamp a commit, and its placeholder is a *path* anchor — which the
# delivery gate now refuses without a commit. A real command is evidence either way.
node "$CLI" report template "$ID" --command "node --version:0" > "$REPORT_FILE"
echo "  wrote a report template to $REPORT_FILE"

step "taskctl issue deliver $ID --report-file report.json  (POSITIVE)"
node "$CLI" issue deliver "$ID" --report-file "$REPORT_FILE"

step "taskctl labels list"
node "$CLI" labels list

step "taskctl actors list"
node "$CLI" actors list

echo
echo "== all install e2e checks passed =="
