#!/usr/bin/env sh
#
# kids-opencode — kid-safe wrapper that launches the own-client TUI.
#
# Distributed via npm as @kidsinai/kids-opencode. End users install via:
#   bun add -g @kidsinai/kids-opencode
# Or via the bootstrap shell installer at:
#   curl -fsSL https://airbotix.ai/install/kids | sh
#
# Phase 2.5 architecture (docs/v2-api-verification.md Q3, 2026-05-16):
# kids-client owns the `opencode serve` subprocess and tails its stderr
# for plugin audit events. This wrapper:
#   1. Bootstrap PATH so freshly-installed binaries are findable.
#   2. Resolve symlinks (npm bin shim) to locate sibling deps.
#   3. Dispatch subcommands (check / register / --update / --shutdown).
#   4. Friendly handler if the AI engine binary is missing.
#   5. Translate --course / --mission flags into env vars.
#   6. AI-disclosure banner (compliance).
#   7. Load OPENCODE_SERVER_PASSWORD.
#   8. exec kids-client; kids-client supervises the AI engine.
set -e

KIDS_OPENCODE_VERSION="0.0.12"
CONFIG="${KIDS_OPENCODE_CONFIG:-$HOME/.config/kids-opencode/opencode.json}"
CONFIG_DIR_WRAPPER="$(dirname "$CONFIG")"

# ────────────────────────────────────────────────────────────────────────────
# 1. PATH bootstrap (idempotent)
# ────────────────────────────────────────────────────────────────────────────
for d in "$HOME/.bun/bin" "$HOME/.opencode/bin" "$HOME/.local/bin"; do
  case ":$PATH:" in
    *":$d:"*) : ;;
    *) [ -d "$d" ] && PATH="$d:$PATH" ;;
  esac
done
export PATH

# ────────────────────────────────────────────────────────────────────────────
# 1b. Source env file written by the setup wizard (ANTHROPIC_API_KEY etc.)
# ────────────────────────────────────────────────────────────────────────────
KIDS_ENV_FILE="$CONFIG_DIR_WRAPPER/env"
if [ -f "$KIDS_ENV_FILE" ]; then
  set -a
  # shellcheck disable=SC1090
  . "$KIDS_ENV_FILE"
  set +a
fi

# ────────────────────────────────────────────────────────────────────────────
# 2. Resolve symlink — npm/bun put a shim in ~/.bun/bin/ pointing at the
# real wrapper inside the installed package. We need the real path to
# find sibling deps (kids-client) in the same node_modules.
# ────────────────────────────────────────────────────────────────────────────
RESOLVED="$0"
while [ -L "$RESOLVED" ]; do
  LINKDIR=$(cd "$(dirname "$RESOLVED")" && pwd)
  LINK=$(readlink "$RESOLVED")
  case "$LINK" in
    /*) RESOLVED="$LINK" ;;
    *)  RESOLVED="$LINKDIR/$LINK" ;;
  esac
done
THIS_DIR=$(cd "$(dirname "$RESOLVED")" && pwd)

# ────────────────────────────────────────────────────────────────────────────
# 3. Subcommand dispatch (before flag parsing)
# ────────────────────────────────────────────────────────────────────────────

case "${1:-}" in
  check)
    shift
    if ! command -v bun >/dev/null 2>&1; then
      echo "Kids OpenCode: required runtime missing." >&2
      echo "Reinstall: bun add -g @kidsinai/kids-opencode" >&2
      exit 1
    fi
    PLUGIN_DIR="$(opencode plugin path @kidsinai/kids-opencode-plugin 2>/dev/null || true)"
    if [ -z "$PLUGIN_DIR" ] || [ ! -d "$PLUGIN_DIR" ]; then
      PLUGIN_DIR="$HOME/.config/opencode/plugins/node_modules/@kidsinai/kids-opencode-plugin"
    fi
    if [ ! -f "$PLUGIN_DIR/src/check-runner.ts" ]; then
      echo "Kids OpenCode: mission validator not found." >&2
      echo "Reinstall: bun add -g @kidsinai/kids-opencode" >&2
      exit 1
    fi
    exec bun "$PLUGIN_DIR/src/check-runner.ts" "$@"
    ;;
  register)
    shift
    CONFIG_DIR_REG="$(dirname "${KIDS_OPENCODE_CONFIG:-$HOME/.config/kids-opencode/opencode.json}")"
    mkdir -p "$CONFIG_DIR_REG"
    chmod 700 "$CONFIG_DIR_REG"
    DEVICE_ID_FILE="$CONFIG_DIR_REG/device-id"
    if [ ! -f "$DEVICE_ID_FILE" ]; then
      if command -v uuidgen >/dev/null 2>&1; then
        uuidgen | tr 'A-Z' 'a-z' > "$DEVICE_ID_FILE"
      else
        od -An -N16 -tx1 /dev/urandom | tr -d ' \n' > "$DEVICE_ID_FILE"
      fi
      chmod 600 "$DEVICE_ID_FILE"
    fi
    DEVICE_ID="$(cat "$DEVICE_ID_FILE")"
    cat <<REGEOF

Kids OpenCode — register your family
─────────────────────────────────────

Step 1: open the parent portal in any browser:
  https://app.airbotix.ai/portal/signup?device=$DEVICE_ID

Step 2: complete parent identity + payment + Kid Profile setup.

Step 3: when the portal shows a 6-character code, paste it here:

REGEOF
    printf "  code: "
    read -r CODE
    if [ -z "$CODE" ]; then
      echo "Kids OpenCode: no code entered; nothing saved." >&2
      exit 1
    fi
    echo "$CODE" > "$CONFIG_DIR_REG/register-code"
    chmod 600 "$CONFIG_DIR_REG/register-code"
    echo "Kids OpenCode: code saved." >&2
    echo "" >&2
    echo "⚠️  Parent-portal tenant-key exchange is still rolling out." >&2
    echo "    Set DEEPROUTER_API_KEY when prompted by your parent dashboard." >&2
    exit 0
    ;;
  --update)
    # V0.0.2: update via npm. Both `bun` and `npm` work; prefer bun.
    if command -v bun >/dev/null 2>&1; then
      echo "Kids OpenCode: pulling latest via bun…" >&2
      exec bun add -g @kidsinai/kids-opencode@latest
    elif command -v npm >/dev/null 2>&1; then
      echo "Kids OpenCode: pulling latest via npm…" >&2
      exec npm install -g @kidsinai/kids-opencode@latest
    else
      echo "Kids OpenCode: neither 'bun' nor 'npm' found; can't self-update." >&2
      echo "Reinstall: curl -fsSL https://airbotix.ai/install/kids | sh" >&2
      exit 1
    fi
    ;;
  --shutdown)
    if command -v lsof >/dev/null 2>&1; then
      pids="$(lsof -ti tcp:4096 2>/dev/null || true)"
      if [ -n "$pids" ]; then
        echo "Kids OpenCode: stopping background AI server (pid: $pids)" >&2
        # shellcheck disable=SC2086
        kill $pids 2>/dev/null || true
      else
        echo "Kids OpenCode: nothing running to stop." >&2
      fi
    else
      echo "Kids OpenCode: 'lsof' not available; stop manually if needed." >&2
    fi
    exit 0
    ;;
  --version|-v)
    echo "kids-opencode $KIDS_OPENCODE_VERSION"
    exit 0
    ;;
  --kids-help|--help|-h)
    cat <<'HELPEOF'
kids-opencode — your kid-safe AI coding mentor.

USAGE
  kids-opencode                    Start a coding session in the current folder
  kids-opencode --course <pack>    Start a guided Course Pack
  kids-opencode --course <pack> --mission <id>
                                   Start at a specific Mission
  kids-opencode check <mission>    Check if you finished a Mission
  kids-opencode register           Register your family (parent does once)
  kids-opencode --update           Update to the latest version
  kids-opencode --shutdown         Stop the background AI server
  kids-opencode --version          Show version

EXAMPLES
  cd ~/my-portfolio
  kids-opencode --course portfolio-site --mission mission-1

  cd ~/my-portfolio
  kids-opencode check mission-1

KEY BINDINGS (inside the session)
  Enter           Send your message
  y / n / e       Reply to AI's request (allow / stop / I'll do it)
  Esc             Interrupt the AI mid-reply
  Ctrl+C          Exit completely

LEARN MORE
  https://airbotix.ai/kids-opencode
HELPEOF
    exit 0
    ;;
esac

# ────────────────────────────────────────────────────────────────────────────
# 3b. Silent auto-update (once per 24h)
# ────────────────────────────────────────────────────────────────────────────
# Runs AFTER subcommand dispatch so explicit `--update`, `--version`,
# `register`, `check`, `--shutdown` are not preempted. Best-effort: any
# network or installer failure is swallowed so the kid still gets to
# start coding. Set KIDS_OPENCODE_NO_AUTO_UPDATE=1 to opt out.

CHECK_INTERVAL=86400  # 24h in seconds
LAST_CHECK_FILE="$CONFIG_DIR_WRAPPER/last-update-check"
mkdir -p "$CONFIG_DIR_WRAPPER" 2>/dev/null || true

should_check_update() {
  [ "${KIDS_OPENCODE_NO_AUTO_UPDATE:-0}" = "1" ] && return 1
  command -v curl >/dev/null 2>&1 || return 1
  if [ -f "$LAST_CHECK_FILE" ]; then
    last=$(cat "$LAST_CHECK_FILE" 2>/dev/null || echo 0)
    [ -z "$last" ] && last=0
    now=$(date +%s)
    age=$((now - last))
    [ "$age" -lt "$CHECK_INTERVAL" ] && return 1
  fi
  return 0
}

if should_check_update; then
  # Write timestamp first — if update install fails, we still wait 24h
  # before retrying so we don't bombard npm or interrupt every launch.
  date +%s > "$LAST_CHECK_FILE" 2>/dev/null || true

  LATEST=$(curl -fsS --max-time 3 https://registry.npmjs.org/@kidsinai/kids-opencode/latest 2>/dev/null \
    | sed -n 's/.*"version":"\([^"]*\)".*/\1/p' | head -1)

  if [ -n "$LATEST" ] && [ "$LATEST" != "$KIDS_OPENCODE_VERSION" ]; then
    echo "Kids OpenCode: new version $LATEST available (you have $KIDS_OPENCODE_VERSION). Updating quietly…" >&2
    UPDATED=0
    if command -v bun >/dev/null 2>&1; then
      bun add -g "@kidsinai/kids-opencode@$LATEST" >/dev/null 2>&1 && UPDATED=1
    elif command -v npm >/dev/null 2>&1; then
      npm install -g "@kidsinai/kids-opencode@$LATEST" >/dev/null 2>&1 && UPDATED=1
    fi
    if [ "$UPDATED" = 1 ]; then
      echo "Kids OpenCode: ✓ updated to $LATEST" >&2
      # Re-exec so the just-installed wrapper takes over (kids-client too,
      # via its updated peer dep). The npm shim still points at the same
      # path, so $0 is safe to re-invoke.
      exec "$0" "$@"
    fi
    # Quiet on failure — kid still gets to start their session.
  fi
fi

# ────────────────────────────────────────────────────────────────────────────
# 4. AI engine check — gracefully handle missing opencode binary
# ────────────────────────────────────────────────────────────────────────────

if ! command -v opencode >/dev/null 2>&1; then
  cat >&2 <<MISSING_ENGINE

Kids OpenCode — almost there!

The AI engine isn't installed yet. Run this one line, then come back:

  curl -fsSL https://opencode.ai/install | sh

Already installed? It might just need a fresh terminal:

  exec ${SHELL:-zsh}

MISSING_ENGINE
  exit 1
fi

# ────────────────────────────────────────────────────────────────────────────
# 5. Flag pre-processing
# ────────────────────────────────────────────────────────────────────────────

FORWARD_ARGS=""
while [ $# -gt 0 ]; do
  case "$1" in
    --course)       shift; export KIDS_COURSE_PACK="${1:-}"; shift ;;
    --course=*)     export KIDS_COURSE_PACK="${1#--course=}"; shift ;;
    --mission)      shift; export KIDS_MISSION="${1:-}"; shift ;;
    --mission=*)    export KIDS_MISSION="${1#--mission=}"; shift ;;
    *)              FORWARD_ARGS="$FORWARD_ARGS $1"; shift ;;
  esac
done

# ────────────────────────────────────────────────────────────────────────────
# 6. Config check + first-run gentle bootstrap
# ────────────────────────────────────────────────────────────────────────────

if [ ! -f "$CONFIG" ]; then
  CONFIG_DIR=$(dirname "$CONFIG")
  mkdir -p "$CONFIG_DIR" && chmod 700 "$CONFIG_DIR" 2>/dev/null || true
  for candidate in \
    "$THIS_DIR/../config/opencode.json.template" \
    "$THIS_DIR/../../kids-opencode/config/opencode.json.template" \
    "$THIS_DIR/../../@kidsinai/kids-opencode/config/opencode.json.template"; do
    if [ -f "$candidate" ]; then
      cp "$candidate" "$CONFIG"
      break
    fi
  done
fi

if [ ! -f "$CONFIG" ]; then
  echo "Kids OpenCode: config not found at $CONFIG" >&2
  echo "Reinstall: bun add -g @kidsinai/kids-opencode" >&2
  exit 1
fi

# ────────────────────────────────────────────────────────────────────────────
# 7. Locate kids-client (PATH OR sibling dep)
# ────────────────────────────────────────────────────────────────────────────

# Prefer the kids-client bundled inside our own package over a global one
# on PATH. Otherwise an older standalone `bun add -g @kidsinai/kids-client`
# silently overrides the version we shipped with this wrapper, and the kid
# gets a wrapper/client version mismatch (e.g. wrapper has auth_choice +
# tour wired but the old client doesn't render them).
KIDS_CLIENT_BIN=""
for candidate in \
  "$THIS_DIR/../../kids-client/bin/kids-client" \
  "$THIS_DIR/../../@kidsinai/kids-client/bin/kids-client" \
  "$THIS_DIR/../node_modules/@kidsinai/kids-client/bin/kids-client" \
  "$THIS_DIR/../../../packages/kids-client/bin/kids-client"; do
  if [ -x "$candidate" ]; then
    KIDS_CLIENT_BIN="$candidate"
    break
  fi
done
if [ -z "$KIDS_CLIENT_BIN" ] && command -v kids-client >/dev/null 2>&1; then
  KIDS_CLIENT_BIN="$(command -v kids-client)"
fi
if [ -z "$KIDS_CLIENT_BIN" ]; then
  echo "Kids OpenCode: kid interface not installed. Reinstall:" >&2
  echo "  bun add -g @kidsinai/kids-opencode" >&2
  exit 1
fi

# ────────────────────────────────────────────────────────────────────────────
# 8. AI-disclosure banner (compliance)
# ────────────────────────────────────────────────────────────────────────────

if [ "${KIDS_OPENCODE_NO_BANNER:-0}" != "1" ]; then
  cat >&2 <<'BANNER'
─────────────────────────────────────────────────────────────────
  Kids OpenCode — you are about to talk to an AI coding mentor.

  The AI is not a real person. It can be wrong. Ask a parent or
  teacher if you are unsure about anything it says.

  Need help in Australia: Kids Helpline 1800 55 1800 (free, 24/7).
─────────────────────────────────────────────────────────────────
BANNER
fi

# ────────────────────────────────────────────────────────────────────────────
# 9. Load server password
# ────────────────────────────────────────────────────────────────────────────

PASSWORD_FILE="$(dirname "$CONFIG")/server-password"
if [ ! -f "$PASSWORD_FILE" ]; then
  # Postinstall didn't run (e.g., --ignore-scripts). Generate now.
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -base64 32 > "$PASSWORD_FILE"
  elif [ -r /dev/urandom ]; then
    head -c 32 /dev/urandom | base64 > "$PASSWORD_FILE"
  else
    echo "Kids OpenCode: cannot generate session password." >&2
    exit 1
  fi
  chmod 600 "$PASSWORD_FILE" 2>/dev/null || true
fi
OPENCODE_SERVER_PASSWORD="$(cat "$PASSWORD_FILE")"
export OPENCODE_SERVER_PASSWORD

# ────────────────────────────────────────────────────────────────────────────
# 10. Hand off to kids-client (with OAuth handoff loop)
# ────────────────────────────────────────────────────────────────────────────
#
# kids-client may exit with KIDS_EXIT_CODE_OAUTH (123) to request that we
# run `opencode auth login --provider <p>` interactively (it needs the
# TTY, which kids-client's Ink TUI is occupying). The KIDS_OAUTH_PROVIDER
# env var (written by saveSetupOauth() into the env file) tells us which
# provider to log into. After OAuth completes, we re-run kids-client; it
# now finds the credentials in opencode's own auth.json store and boots
# straight to MissionScreen.

export KIDS_OPENCODE_CONFIG="$CONFIG"
OPENCODE_BIN="$(command -v opencode)"
export OPENCODE_BIN

KIDS_EXIT_CODE_OAUTH=123
OAUTH_ATTEMPTS=0
OAUTH_MAX_ATTEMPTS=2

while :; do
  set +e
  # shellcheck disable=SC2086 # we want word-splitting on FORWARD_ARGS
  "$KIDS_CLIENT_BIN" $FORWARD_ARGS
  RC=$?
  set -e

  if [ "$RC" -ne "$KIDS_EXIT_CODE_OAUTH" ]; then
    exit "$RC"
  fi

  OAUTH_ATTEMPTS=$((OAUTH_ATTEMPTS + 1))
  if [ "$OAUTH_ATTEMPTS" -gt "$OAUTH_MAX_ATTEMPTS" ]; then
    echo "Kids OpenCode: too many OAuth handoffs in one run; aborting." >&2
    exit 1
  fi

  # Re-source the env file — kids-client just wrote KIDS_OAUTH_PROVIDER.
  if [ -f "$KIDS_ENV_FILE" ]; then
    set -a
    # shellcheck disable=SC1090
    . "$KIDS_ENV_FILE"
    set +a
  fi

  if [ -z "${KIDS_OAUTH_PROVIDER:-}" ]; then
    echo "Kids OpenCode: OAuth handoff requested but KIDS_OAUTH_PROVIDER is empty." >&2
    exit 1
  fi

  cat >&2 <<OAUTHMSG

Kids OpenCode — opening login for $KIDS_OAUTH_PROVIDER…

You're about to see the opencode auth flow take over the screen. Finish
signing in (a browser tab will open) and we'll pick back up automatically.

OAUTHMSG

  # Pass --method too if the setup wizard staged one (KIDS_OAUTH_METHOD).
  # Without it opencode would still show its own method picker and the kid
  # would be confused: "I already chose Pro/Plus in the wizard, why ask?"
  set +e
  if [ -n "${KIDS_OAUTH_METHOD:-}" ]; then
    opencode auth login --provider "$KIDS_OAUTH_PROVIDER" --method "$KIDS_OAUTH_METHOD"
  else
    opencode auth login --provider "$KIDS_OAUTH_PROVIDER"
  fi
  AUTH_RC=$?
  set -e

  if [ "$AUTH_RC" -ne 0 ]; then
    echo "Kids OpenCode: opencode auth login exited $AUTH_RC. Run kids-opencode again to retry." >&2
    exit "$AUTH_RC"
  fi
done
