#!/usr/bin/env bash
# Every exit code in sw.sh's contract, reachable with the daemon, ccr and claude stubbed.
# audience: AI coding agents first. No browser, no ccr, no ChatGPT turn is spent here.
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SW="$HERE/sw.sh"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

PASS=0 FAIL=0
check() { # check <name> <expected-rc> <actual-rc> [haystack] [needle]
  local name="$1" want="$2" got="$3" hay="${4:-}" needle="${5:-}"
  if [[ "$got" != "$want" ]]; then
    echo "FAIL $name: expected exit $want, got $got"; FAIL=$((FAIL + 1)); return
  fi
  if [[ -n "$needle" && "$hay" != *"$needle"* ]]; then
    echo "FAIL $name: output missing ${needle}"; echo "  got: ${hay:0:300}"; FAIL=$((FAIL + 1)); return
  fi
  echo "ok   $name (exit $got)"; PASS=$((PASS + 1))
}

WORKSPACE="$TMP/ws"; mkdir -p "$WORKSPACE"
LOGS="$TMP/logs"
TOKEN_FILE="$TMP/token"; printf 'stub-token\n' > "$TOKEN_FILE"
PORT=8794

# --- stubs ------------------------------------------------------------------
STUBS="$TMP/bin"; mkdir -p "$STUBS"

make_claude() { # make_claude <exit-code> <stdout> [sleep-seconds]
  cat > "$STUBS/claude" <<EOF
#!/usr/bin/env bash
: > "$TMP/claude-ran"
sleep ${3:-0}
printf '%s\n' '$2'
exit $1
EOF
  chmod +x "$STUBS/claude"
}

ccr_stub() { # ccr_stub <up:true|false>
  printf '#!/usr/bin/env bash\necho %s\n' "'{\"up\":$1,\"detail\":\"stub\"}'" > "$TMP/ccr-up.sh"
  chmod +x "$TMP/ccr-up.sh"
}

solwebd_stub() { # solwebd_stub <startable:yes|no>
  if [[ "$1" == "yes" ]]; then
    printf '#!/usr/bin/env bash\nexec python3 %q --port %q\n' "$TMP/solwebd.py" "$PORT" > "$TMP/solwebd"
  else
    printf '#!/usr/bin/env bash\nexit 1\n' > "$TMP/solwebd"
  fi
  chmod +x "$TMP/solwebd"
}

cat > "$TMP/solwebd.py" <<'PY'
import json, sys, threading, time
from http.server import BaseHTTPRequestHandler, HTTPServer

class H(BaseHTTPRequestHandler):
    def log_message(self, *a): pass
    def _ok(self, payload):
        body = json.dumps(payload).encode()
        self.send_response(200); self.send_header("Content-Length", str(len(body)))
        self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(body)
    def do_GET(self):
        self._ok({"ok": True, "conversations": 0, "queue": 0, "turn_p50_s": 0.0})
    def do_POST(self):
        self._ok({"ok": True})
        threading.Thread(target=self.server.shutdown, daemon=True).start()

server = HTTPServer(("127.0.0.1", int(sys.argv[2])), H)
server.serve_forever()
# Outlive the closed port, the way the real browser teardown does.
time.sleep(3)
PY

run_sw() { # run_sw <args...> -> prints output, sets RC
  OUT="$(PATH="$STUBS:$PATH" \
    _SW_CCR_UP_BIN="$TMP/ccr-up.sh" \
    _SW_SOLWEBD_BIN="$TMP/solwebd" \
    SOLWEBD_PORT="$PORT" \
    SOLWEBD_TOKEN_FILE="$TOKEN_FILE" \
    SW_LOG_DIR="$LOGS" \
    SW_CLAUDE_CONFIG_DIR="$TMP/cc-config" \
    CCR_CONFIG_FILE="$TMP/ccr.json" \
    bash "$SW" "$@" 2>&1)"
  RC=$?
}

ccr_config() { # ccr_config <with-solweb-provider:yes|no>
  if [[ "$1" == "yes" ]]; then
    printf '{"PORT":3456,"APIKEY":"stub-key","Providers":[{"name":"solweb","models":["sol-web-high"]}]}\n' > "$TMP/ccr.json"
  else
    printf '{"PORT":3456,"APIKEY":"stub-key","Providers":[{"name":"openrouter","models":["x"]}]}\n' > "$TMP/ccr.json"
  fi
}
ccr_config yes

# --- 2: usage errors --------------------------------------------------------
ccr_stub true; solwebd_stub yes; make_claude 0 '{"ok":true}'
run_sw --trust "do a thing" --task-slug t
check "missing --workspace is a usage error" 2 "$RC" "$OUT" '"ok":false'

run_sw --workspace "$WORKSPACE" --task-slug t
check "missing --trust is a usage error" 2 "$RC" "$OUT" '"ok":false'

run_sw --workspace "$WORKSPACE" --trust "do a thing"
check "missing --task-slug is a usage error" 2 "$RC" "$OUT" '"ok":false'

run_sw --workspace "$WORKSPACE" --trust x --task-slug t --model sol-web-turbo
check "unknown effort suffix is a usage error" 2 "$RC" "$OUT" 'sol-web-turbo'

run_sw --workspace "$WORKSPACE" --trust x --task-slug t --model
check "a flag with no value is a usage error" 2 "$RC" "$OUT" 'requires a value'

# --- 3: nothing to dispatch to ---------------------------------------------
solwebd_stub no; ccr_stub true
rm -f "$TMP/claude-ran"
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
check "unstartable solwebd exits 3" 3 "$RC" "$OUT" 'solwebd'
[[ ! -f "$TMP/claude-ran" ]]; check "no dispatch happened when solwebd is down" 0 "$?"

solwebd_stub yes; ccr_stub false
rm -f "$TMP/claude-ran"
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
check "ccr reporting down exits 3" 3 "$RC" "$OUT" 'ccr not up'
[[ ! -f "$TMP/claude-ran" ]]; check "no dispatch happened when ccr is down" 0 "$?"

ccr_stub true; ccr_config no
rm -f "$TMP/claude-ran"
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
check "ccr without the solweb provider exits 3" 3 "$RC" "$OUT" 'install-ccr-provider'
[[ ! -f "$TMP/claude-ran" ]]; check "no dispatch happened without the provider" 0 "$?"
ccr_config yes

# The marker must actually be written when a dispatch DOES happen, or the two
# no-dispatch checks above would pass against a stub that never runs at all.
ccr_stub true; make_claude 0 '{"ok":true}'
rm -f "$TMP/claude-ran"
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
[[ -f "$TMP/claude-ran" ]]; check "the dispatch marker proves the stub really runs" 0 "$?"

# --- 0 / 124 / 75: the dispatch itself -------------------------------------
ccr_stub true; solwebd_stub yes

make_claude 0 '{"result":"created hello.txt"}'
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
check "a completed run exits 0" 0 "$RC" "$OUT" '"ok":true'

make_claude 0 '{"result":"done"}' 6
run_sw --workspace "$WORKSPACE" --trust x --task-slug t --timeout 2
check "a run past its timeout exits 124" 124 "$RC"

make_claude 1 '{"error":"429 usage limit reached, try again at 3:45 PM"}'
run_sw --workspace "$WORKSPACE" --trust x --task-slug t
check "a usage cap exits 75 with resume_at" 75 "$RC" "$OUT" 'resume_at'

# --- the health surface -----------------------------------------------------
run_sw --health
check "--health exits 0 when both hops are up" 0 "$RC" "$OUT" '"ok": true'

ccr_stub false
run_sw --health
check "--health exits 3 when ccr is down" 3 "$RC"

# --- stopping the daemon ----------------------------------------------------
STARTED=$SECONDS
run_sw --stop
check "--stop stops a running daemon" 0 "$RC" "$OUT" 'solwebd stopped'
[[ $((SECONDS - STARTED)) -ge 3 ]]; check "--stop waits for the process, not just the port" 0 "$?"
pgrep -f "$TMP/solwebd.py" >/dev/null; [[ $? -ne 0 ]]; check "no daemon process survives --stop" 0 "$?"

run_sw --stop
check "--stop on a stopped daemon is still 0" 0 "$RC" "$OUT" 'was not running'

pkill -f "$TMP/solwebd.py" 2>/dev/null
echo
echo "passed=$PASS failed=$FAIL"
[[ $FAIL -eq 0 ]]
