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

log() {
  echo "[openclaw-docker-ui] $*"
}

fail() {
  echo "[openclaw-docker-ui] ERROR: $*" >&2
  exit 1
}

require_cmd() {
  local cmd="$1"
  command -v "$cmd" >/dev/null 2>&1 || fail "missing required command: $cmd"
}

reset_openclaw_state_dir() {
  local state_dir="$1"
  local resolved_state_dir resolved_home

  [[ -n "$state_dir" ]] || fail "OPENCLAW_CONFIG_DIR must not be empty when resetting state"
  mkdir -p "$state_dir"

  resolved_state_dir="$(cd "$state_dir" && pwd -P)"
  resolved_home="$(cd "$HOME" && pwd -P)"
  case "$resolved_state_dir" in
    "/"|"$resolved_home")
      fail "refusing to reset unsafe OPENCLAW_CONFIG_DIR: $resolved_state_dir"
      ;;
  esac

  log "resetting OpenClaw state under $resolved_state_dir"
  rm -rf \
    "$resolved_state_dir/agents" \
    "$resolved_state_dir/canvas" \
    "$resolved_state_dir/cron" \
    "$resolved_state_dir/credentials" \
    "$resolved_state_dir/devices" \
    "$resolved_state_dir/identity" \
    "$resolved_state_dir/logs" \
    "$resolved_state_dir/memory" \
    "$resolved_state_dir/skills" \
    "$resolved_state_dir/workspace"
  rm -f \
    "$resolved_state_dir/openclaw.json" \
    "$resolved_state_dir/openclaw.json.bak" \
    "$resolved_state_dir/update-check.json"
}

require_cmd docker
require_cmd git
require_cmd curl
require_cmd openssl
require_cmd grep

OPENCLAW_REPO_URL="${OPENCLAW_REPO_URL:-https://github.com/openclaw/openclaw.git}"
OPENCLAW_DOCKER_DIR="${OPENCLAW_DOCKER_DIR:-/tmp/openclaw-docker}"
OPENCLAW_REPO_REF="${OPENCLAW_REPO_REF:-v2026.3.2}"
OPENCLAW_IMAGE="${OPENCLAW_IMAGE:-openclaw:local}"
OPENCLAW_TMP_DIR="${OPENCLAW_TMP_DIR:-${TMPDIR:-/tmp}}"
OPENCLAW_TMP_DIR="${OPENCLAW_TMP_DIR%/}"
OPENCLAW_TMP_DIR="${OPENCLAW_TMP_DIR:-/tmp}"
OPENCLAW_CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$OPENCLAW_TMP_DIR/openclaw-paperclip-smoke}"
OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$OPENCLAW_CONFIG_DIR/workspace}"
OPENCLAW_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-18789}"
OPENCLAW_BRIDGE_PORT="${OPENCLAW_BRIDGE_PORT:-18790}"
OPENCLAW_GATEWAY_BIND="${OPENCLAW_GATEWAY_BIND:-lan}"
OPENCLAW_GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-$(openssl rand -hex 32)}"
OPENCLAW_BUILD="${OPENCLAW_BUILD:-1}"
OPENCLAW_WAIT_SECONDS="${OPENCLAW_WAIT_SECONDS:-45}"
OPENCLAW_OPEN_BROWSER="${OPENCLAW_OPEN_BROWSER:-0}"
OPENCLAW_SECRETS_FILE="${OPENCLAW_SECRETS_FILE:-$HOME/.secrets}"
# Keep default one-command UX: local smoke run should not require manual pairing.
OPENCLAW_DISABLE_DEVICE_AUTH="${OPENCLAW_DISABLE_DEVICE_AUTH:-1}"
OPENCLAW_MODEL_PRIMARY="${OPENCLAW_MODEL_PRIMARY:-openai/gpt-5.2}"
OPENCLAW_MODEL_FALLBACK="${OPENCLAW_MODEL_FALLBACK:-openai/gpt-5.2-chat-latest}"
OPENCLAW_RESET_STATE="${OPENCLAW_RESET_STATE:-1}"
PAPERCLIP_HOST_PORT="${PAPERCLIP_HOST_PORT:-3100}"
PAPERCLIP_HOST_FROM_CONTAINER="${PAPERCLIP_HOST_FROM_CONTAINER:-host.docker.internal}"

case "$OPENCLAW_DISABLE_DEVICE_AUTH" in
  1|true|TRUE|True|yes|YES|Yes)
    OPENCLAW_DISABLE_DEVICE_AUTH_JSON="true"
    ;;
  0|false|FALSE|False|no|NO|No)
    OPENCLAW_DISABLE_DEVICE_AUTH_JSON="false"
    ;;
  *)
    fail "OPENCLAW_DISABLE_DEVICE_AUTH must be one of: 1,0,true,false,yes,no"
    ;;
esac

if [[ -z "${OPENAI_API_KEY:-}" && -f "$OPENCLAW_SECRETS_FILE" ]]; then
  set +u
  # shellcheck source=/dev/null
  source "$OPENCLAW_SECRETS_FILE"
  set -u
fi

[[ -n "${OPENAI_API_KEY:-}" ]] || fail "OPENAI_API_KEY is required (set env var or include it in $OPENCLAW_SECRETS_FILE)"

log "preparing OpenClaw repo at $OPENCLAW_DOCKER_DIR"
if [[ -d "$OPENCLAW_DOCKER_DIR/.git" ]]; then
  git -C "$OPENCLAW_DOCKER_DIR" fetch --quiet --tags origin || true
else
  rm -rf "$OPENCLAW_DOCKER_DIR"
  git clone "$OPENCLAW_REPO_URL" "$OPENCLAW_DOCKER_DIR"
  git -C "$OPENCLAW_DOCKER_DIR" fetch --quiet --tags origin || true
fi

resolved_openclaw_ref=""
if git -C "$OPENCLAW_DOCKER_DIR" rev-parse --verify --quiet "origin/$OPENCLAW_REPO_REF" >/dev/null; then
  resolved_openclaw_ref="origin/$OPENCLAW_REPO_REF"
elif git -C "$OPENCLAW_DOCKER_DIR" rev-parse --verify --quiet "$OPENCLAW_REPO_REF" >/dev/null; then
  resolved_openclaw_ref="$OPENCLAW_REPO_REF"
fi
[[ -n "$resolved_openclaw_ref" ]] || fail "unable to resolve OPENCLAW_REPO_REF=$OPENCLAW_REPO_REF in $OPENCLAW_DOCKER_DIR"
git -C "$OPENCLAW_DOCKER_DIR" checkout --quiet "$resolved_openclaw_ref"
log "using OpenClaw ref $resolved_openclaw_ref ($(git -C "$OPENCLAW_DOCKER_DIR" rev-parse --short HEAD))"

if [[ "$OPENCLAW_BUILD" == "1" ]]; then
  log "building Docker image $OPENCLAW_IMAGE"
  docker build -t "$OPENCLAW_IMAGE" -f "$OPENCLAW_DOCKER_DIR/Dockerfile" "$OPENCLAW_DOCKER_DIR"
fi

log "writing OpenClaw config under $OPENCLAW_CONFIG_DIR"
if [[ "$OPENCLAW_RESET_STATE" == "1" ]]; then
  # Ensure deterministic smoke behavior across reruns by restarting with a clean state dir.
  OPENCLAW_CONFIG_DIR="$OPENCLAW_CONFIG_DIR" \
    OPENCLAW_WORKSPACE_DIR="$OPENCLAW_WORKSPACE_DIR" \
    OPENCLAW_GATEWAY_PORT="$OPENCLAW_GATEWAY_PORT" \
    OPENCLAW_BRIDGE_PORT="$OPENCLAW_BRIDGE_PORT" \
    OPENCLAW_GATEWAY_BIND="$OPENCLAW_GATEWAY_BIND" \
    OPENCLAW_GATEWAY_TOKEN="$OPENCLAW_GATEWAY_TOKEN" \
    OPENCLAW_IMAGE="$OPENCLAW_IMAGE" \
    OPENAI_API_KEY="$OPENAI_API_KEY" \
    docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" down --remove-orphans >/dev/null 2>&1 || true
  reset_openclaw_state_dir "$OPENCLAW_CONFIG_DIR"
fi
mkdir -p "$OPENCLAW_WORKSPACE_DIR" "$OPENCLAW_CONFIG_DIR/identity" "$OPENCLAW_CONFIG_DIR/credentials"
chmod 700 "$OPENCLAW_CONFIG_DIR" "$OPENCLAW_CONFIG_DIR/credentials"

cat > "$OPENCLAW_CONFIG_DIR/openclaw.json" <<EOF
{
  "gateway": {
    "mode": "local",
    "port": ${OPENCLAW_GATEWAY_PORT},
    "bind": "${OPENCLAW_GATEWAY_BIND}",
    "auth": {
      "mode": "token",
      "token": "${OPENCLAW_GATEWAY_TOKEN}"
    },
    "controlUi": {
      "enabled": true,
      "dangerouslyDisableDeviceAuth": ${OPENCLAW_DISABLE_DEVICE_AUTH_JSON},
      "allowedOrigins": [
        "http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}",
        "http://localhost:${OPENCLAW_GATEWAY_PORT}"
      ]
    }
  },
  "env": {
    "OPENAI_API_KEY": "${OPENAI_API_KEY}"
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "${OPENCLAW_MODEL_PRIMARY}",
        "fallbacks": [
          "${OPENCLAW_MODEL_FALLBACK}"
        ]
      },
      "workspace": "/home/node/.openclaw/workspace"
    }
  }
}
EOF
chmod 600 "$OPENCLAW_CONFIG_DIR/openclaw.json"

cat > "$OPENCLAW_DOCKER_DIR/.env" <<EOF
OPENCLAW_CONFIG_DIR=$OPENCLAW_CONFIG_DIR
OPENCLAW_WORKSPACE_DIR=$OPENCLAW_WORKSPACE_DIR
OPENCLAW_GATEWAY_PORT=$OPENCLAW_GATEWAY_PORT
OPENCLAW_BRIDGE_PORT=$OPENCLAW_BRIDGE_PORT
OPENCLAW_GATEWAY_BIND=$OPENCLAW_GATEWAY_BIND
OPENCLAW_GATEWAY_TOKEN=$OPENCLAW_GATEWAY_TOKEN
OPENCLAW_IMAGE=$OPENCLAW_IMAGE
OPENAI_API_KEY=$OPENAI_API_KEY
EOF

COMPOSE_OVERRIDE="${OPENCLAW_DOCKER_DIR}/.paperclip-openclaw.override.yml"
cat > "$COMPOSE_OVERRIDE" <<EOF
services:
  openclaw-gateway:
    tmpfs:
      - /tmp:exec,size=512M
    extra_hosts:
      - "host.docker.internal:host-gateway"
  openclaw-cli:
    tmpfs:
      - /tmp:exec,size=512M
EOF

compose() {
  docker compose \
    -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" \
    -f "$COMPOSE_OVERRIDE" \
    "$@"
}

detect_paperclip_base_url() {
  local bridge_gateway candidate health_url
  bridge_gateway="$(docker network inspect openclaw-docker_default --format '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null || true)"
  for candidate in "$PAPERCLIP_HOST_FROM_CONTAINER" "$bridge_gateway"; do
    [[ -n "$candidate" ]] || continue
    health_url="http://${candidate}:${PAPERCLIP_HOST_PORT}/api/health"
    if compose exec -T openclaw-gateway node -e "fetch('${health_url}').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" >/dev/null 2>&1; then
      echo "http://${candidate}:${PAPERCLIP_HOST_PORT}"
      return 0
    fi
  done
  return 1
}

log "starting OpenClaw gateway container"
compose up -d openclaw-gateway

log "waiting for gateway health on http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}/"
READY="0"
for _ in $(seq 1 "$OPENCLAW_WAIT_SECONDS"); do
  code="$(curl -sS -o /dev/null -w "%{http_code}" "http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}/" || true)"
  if [[ "$code" == "200" ]]; then
    READY="1"
    break
  fi
  sleep 1
done
if [[ "$READY" != "1" ]]; then
  compose logs --tail=100 openclaw-gateway || true
  fail "gateway did not become healthy in ${OPENCLAW_WAIT_SECONDS}s"
fi

paperclip_base_url="$(detect_paperclip_base_url || true)"
dashboard_output="$(compose run --rm openclaw-cli dashboard --no-open)"
dashboard_url="$(grep -Eo 'https?://[^[:space:]]+#token=[^[:space:]]+' <<<"$dashboard_output" | head -n1 || true)"
if [[ -z "$dashboard_url" ]]; then
  dashboard_url="http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}/#token=${OPENCLAW_GATEWAY_TOKEN}"
fi

cat <<EOF

OpenClaw gateway is running.

Dashboard URL:
$dashboard_url
EOF

if [[ "$OPENCLAW_DISABLE_DEVICE_AUTH_JSON" == "true" ]]; then
  cat <<EOF
Pairing:
  Device pairing is disabled by default for this local smoke run.
  No extra env vars are required for the default path.
  (Security tradeoff: enable pairing with OPENCLAW_DISABLE_DEVICE_AUTH=0.)
Model:
  ${OPENCLAW_MODEL_PRIMARY} (fallback: ${OPENCLAW_MODEL_FALLBACK})
State:
  OPENCLAW_RESET_STATE=$OPENCLAW_RESET_STATE
Paperclip URL for OpenClaw container:
EOF
  if [[ -n "$paperclip_base_url" ]]; then
    cat <<EOF
  $paperclip_base_url
  (Use this base URL for invite/onboarding links from inside OpenClaw Docker.)
EOF
  else
    cat <<EOF
  Auto-detect failed. Try: http://host.docker.internal:${PAPERCLIP_HOST_PORT}
  (Do not use http://127.0.0.1:${PAPERCLIP_HOST_PORT} inside the container.)
  If Paperclip rejects the host, run on host machine:
    pnpm paperclipai allowed-hostname host.docker.internal
  Then restart Paperclip and re-run this script.
EOF
  fi
  cat <<EOF

Useful commands:
  docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" logs -f openclaw-gateway
  docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" down
EOF
else
  cat <<EOF
Pairing:
  Device pairing is enabled.
  If UI shows "pairing required", run:
    docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" run --rm openclaw-cli devices list
    docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" run --rm openclaw-cli devices approve --latest
Model:
  ${OPENCLAW_MODEL_PRIMARY} (fallback: ${OPENCLAW_MODEL_FALLBACK})
State:
  OPENCLAW_RESET_STATE=$OPENCLAW_RESET_STATE
Paperclip URL for OpenClaw container:
EOF
  if [[ -n "$paperclip_base_url" ]]; then
    cat <<EOF
  $paperclip_base_url
  (Use this base URL for invite/onboarding links from inside OpenClaw Docker.)
EOF
  else
    cat <<EOF
  Auto-detect failed. Try: http://host.docker.internal:${PAPERCLIP_HOST_PORT}
  (Do not use http://127.0.0.1:${PAPERCLIP_HOST_PORT} inside the container.)
  If Paperclip rejects the host, run on host machine:
    pnpm paperclipai allowed-hostname host.docker.internal
  Then restart Paperclip and re-run this script.
EOF
  fi
  cat <<EOF

Useful commands:
  docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" logs -f openclaw-gateway
  docker compose -f "$OPENCLAW_DOCKER_DIR/docker-compose.yml" -f "$COMPOSE_OVERRIDE" down
EOF
fi

if [[ "$OPENCLAW_OPEN_BROWSER" == "1" ]] && command -v open >/dev/null 2>&1; then
  log "opening dashboard in browser"
  open "$dashboard_url"
fi
