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

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
compose=(docker compose -f "${root}/docker-compose.delivery-split.yml")
gateway_url="http://127.0.0.1:${AGENTTEAM_DELIVERY_SPLIT_PORT:-28083}"
advertised_gateway_url="http://localhost:${AGENTTEAM_DELIVERY_SPLIT_PORT:-28083}"
ops_url="http://127.0.0.1:${AGENTTEAM_DELIVERY_SPLIT_OPS_PORT:-28087}"
maas_url="http://127.0.0.1:${AGENTTEAM_DELIVERY_SPLIT_MAAS_PORT:-28088}"
auth_header="Authorization: token delivery-route-token"
session_auth_header="Authorization: Bearer AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8"
ags_base="/api/v1/workspaces/wsp_delivery_a/ags"

internal_http_status() {
  local host="$1" request_path="$2" route_workspace_id="${3:-}" authorization="${4:-${session_auth_header}}"
  "${compose[@]}" exec -T agentteam-gateway bash -ec '
    host="$1"
    request_path="$2"
    route_workspace_id="$3"
    authorization="$4"
    exec 3<>"/dev/tcp/${host}/3001"
    printf "GET %s HTTP/1.0\r\nHost: %s\r\n%s\r\n" "${request_path}" "${host}" "${authorization}" >&3
    if [[ -n "${route_workspace_id}" ]]; then
      printf "X-AgentTeam-Route-Workspace-Id: %s\r\n" "${route_workspace_id}" >&3
    fi
    printf "\r\n" >&3
    read -r _ status _ <&3
    printf "%s\n" "${status}"
  ' bash "${host}" "${request_path}" "${route_workspace_id}" "${authorization}"
}

assert_gateway_404() {
  local method="$1" request_path="$2"
  local status
  status="$(curl --path-as-is -sS -o /dev/null -w '%{http_code}' -X "${method}" \
    "${gateway_url}${request_path}")"
  if [[ "${status}" != "404" ]]; then
    echo "split route ${method} ${request_path} returned ${status}, expected 404" >&2
    exit 1
  fi
}

cleanup() {
  if [[ "${AGENTTEAM_DELIVERY_SPLIT_KEEP:-0}" != "1" ]]; then
    "${compose[@]}" down -v >/dev/null
  fi
}
trap cleanup EXIT

"${compose[@]}" config --quiet
"${compose[@]}" up -d --build

for _ in $(seq 1 80); do
  if curl -fsS "${gateway_url}/readyz" >/dev/null; then
    break
  fi
  sleep 2
done
curl -fsS "${gateway_url}/readyz" >/dev/null
curl -fsS "${maas_url}/healthz" >/dev/null
curl -fsS "${ops_url}/readyz" >/dev/null

# Daemon bootstrap first resolves its machine key on central, then uses the
# returned workspace id for the workspace-owned websocket path.
machine_auth="$(curl -fsS -H 'Content-Type: application/json' \
  --data "{\"apiKey\":\"delivery-machine-api-key\",\"serverUrl\":\"${advertised_gateway_url}\"}" \
  "${gateway_url}/api/v1/machine-auth-resolutions")"
python3 -c 'import json,sys; p=json.load(sys.stdin); assert p["machineId"] == "mac_delivery"; assert p["workspaceId"] == "wsp_delivery_a"' \
  <<<"${machine_auth}"
# Legacy daemons remain compatible both when the machine credential is the
# only workspace association and when an old client sends an explicit query
# discriminator.
curl -fsS "${gateway_url}/daemon/upgrade-info?key=delivery-machine-api-key" | grep -q '"serverVersion"'
curl -fsS "${gateway_url}/daemon/upgrade-info?workspaceId=wsp_delivery_a&key=delivery-machine-api-key" | grep -q '"serverVersion"'

# The only AgentTeam user entrypoint is ingress: static Web assets stay in agentteam-web,
# while readiness/API/realtime/data-plane paths go to the Gateway.
web_index="$(curl -fsS "${gateway_url}/")"
grep -q '<div id="root"></div>' <<<"${web_index}"
for web_history_path in /login /settings; do
  curl -fsS "${gateway_url}${web_history_path}" | grep -q '<div id="root"></div>'
done
web_asset="$(grep -oE '/assets/[^" ]+\.js' <<<"${web_index}" | head -1)"
if [[ -z "${web_asset}" ]]; then
  echo "delivery split Web index did not reference a JavaScript asset" >&2
  exit 1
fi
curl -fsS "${gateway_url}${web_asset}" >/dev/null
curl -fsS "${ops_url}/" | grep -qi '<html'

# Authenticate through the same Ops entrypoint a browser uses, then exercise
# both private admin clients. AgentTeam placement admin must go directly to central;
# private MeshX admin must go directly to the workspace-default Service.
ops_login="$(curl -fsS -H 'Content-Type: application/json' \
  --data '{"email":"admin@agentteam.local","password":"agentteam-local-admin"}' \
  "${ops_url}/api/ops/auth/account/login")"
ops_token="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["accessToken"])' <<<"${ops_login}")"
ops_auth_header="Authorization: Bearer ${ops_token}"
curl -fsS -H "${ops_auth_header}" "${ops_url}/api/ops/workspace-groups" | grep -q 'workspace-default'
curl -fsS -H "${ops_auth_header}" -H 'Content-Type: application/json' --data '{}' \
  "${ops_url}/api/ops/workspace-placements/verify" >/dev/null
curl -fsS -H "${ops_auth_header}" "${ops_url}/api/ops/public-repository-caches?limit=1" >/dev/null

# The split edge exposes no AGS compatibility surface merely for discovery,
# CLI, documentation, telemetry, OAuth, or smoke probes. Only separately
# approved AgentTeam product routes may cross an upstream boundary.
for unsupported_request in \
  "GET /api/v3" \
  "GET /api/v3/" \
  "GET /api/v3/openapi.json" \
  "GET /api/ext/v1" \
  "GET /api/ext/v1/" \
  "GET /api/ext/v1/openapi.json" \
  "GET /api/v3/user" \
  "GET /api/ext/v1/user/tokens" \
  "GET ${ags_base}/api/v3/user" \
  "GET ${ags_base}/api/ext/v1/user/tokens" \
  "GET /api/v3/meta" \
  "GET /api/v3/rate_limit" \
  "POST /api/ext/v1/analytics/events" \
  "POST /api/ext/v1/agents" \
  "POST /api/ext/v1/oidc/device/code" \
  "POST /api/ext/v1/oidc/session" \
  "POST /login/device/code" \
  "POST /login/oauth/access_token" \
  "GET /login/oauth/authorize" \
  "GET /auth/connected/login" \
  "OPTIONS /api/v3/openapi.json" \
  "POST /api/v3/openapi.json" \
  "GET /api/v3/openapi.json/extra" \
  "GET /api/v3//"
do
  read -r unsupported_method unsupported_route <<<"${unsupported_request}"
  assert_gateway_404 "${unsupported_method}" "${unsupported_route}"
done

# Separately activated AgentTeam product REST, raw, and Git requests resolve a
# trustworthy workspace before placement. AGS keeps its existing credential
# and repository authorization.
repo_json="$(curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo")"
grep -q '"full_name":"delivery-a/repo"' <<<"${repo_json}"
avatar_url="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["owner"]["avatar_url"])' <<<"${repo_json}")"
if [[ "${avatar_url}" != "${advertised_gateway_url}${ags_base}/avatars/delivery-a" ]]; then
  echo "workspace AGS response published unexpected avatar URL ${avatar_url}" >&2
  exit 1
fi
avatar_headers="$(curl -sS -D - -o /dev/null "${avatar_url}")"
grep -q '^HTTP/.* 302' <<<"${avatar_headers}"
grep -qi '^Location: https://avatars.githubusercontent.com/u/1?v=4' <<<"${avatar_headers}"

curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo/contents/smoke.txt?ref=main" | \
  python3 -c 'import base64,json,sys; p=json.load(sys.stdin); assert base64.b64decode(p["content"]).decode().strip() == "delivery-split-smoke"'
curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/api/ext/v1/repos/delivery-a/repo/summary" | grep -q '"full_name":"delivery-a/repo"'
curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo/assignees" >/dev/null
main_sha="$(curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo/git/ref/heads/main" | \
  python3 -c 'import json,sys; print(json.load(sys.stdin)["object"]["sha"])')"
curl -fsS -H "${auth_header}" -H 'Content-Type: application/json' \
  --data "{\"ref\":\"refs/tags/delivery-split-backup\",\"sha\":\"${main_sha}\"}" \
  "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo/git/refs" >/dev/null
curl -fsS -H "${auth_header}" \
  "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo/archive/refs/tags/delivery-split-backup.zip" | \
  python3 -c 'import sys; data=sys.stdin.buffer.read(4); assert data.startswith(b"PK")'
# Explicitly activated read-only session-local AGS REST uses authenticated
# workspace placement. Current-user and user-token compatibility remain 404
# because no AgentTeam product caller owns them.
curl -fsS -H "${session_auth_header}" "${gateway_url}${ags_base}/api/v3/user/repos" | grep -q '"full_name":"delivery-a/repo"'
curl -fsS -H "${session_auth_header}" "${gateway_url}${ags_base}/api/v3/search/code?q=delivery-split-smoke" >/dev/null
curl -fsS -H "${session_auth_header}" "${gateway_url}${ags_base}/api/v3/search/labels?repository_id=9101&q=delivery" >/dev/null
graphql_status="$(curl -sS -o /dev/null -w '%{http_code}' -H "${auth_header}" -H 'Content-Type: application/json' \
  --data '{"query":"{ viewer { login } }"}' "${gateway_url}/api/graphql")"
if [[ "${graphql_status}" != "404" ]]; then
  echo "unsupported split GraphQL returned ${graphql_status}, expected 404" >&2
  exit 1
fi
curl -fsS -H "${auth_header}" "${gateway_url}${ags_base}/delivery-a/repo/raw/main/smoke.txt" | grep -qx 'delivery-split-smoke'
curl -fsS -H "${auth_header}" \
  "${gateway_url}${ags_base}/delivery-a/repo.git/info/refs?service=git-upload-pack" | grep -a -q '# service=git-upload-pack'

# A conflicting client workspace hint cannot override credential authority.
curl -fsS -H "${auth_header}" -H "X-AgentTeam-Route-Workspace-Id: wsp_delivery_b" \
  "${gateway_url}${ags_base}/api/v3/repos/delivery-a/repo" >/dev/null

# Deferred AGS repository-management surfaces reject at the edge.
for unsupported_request in \
  "GET /api/v3/repos/delivery-a/repo/collaborators" \
  "GET /api/v3/repositories/9101/environments" \
  "POST /api/v3/user/repos" \
  "POST /api/v3/orgs/delivery-a/repos" \
  "POST /api/v3/repos/delivery-a/repo/pages" \
  "POST /api/ext/v1/repos/delivery-a/repo/team-sharing/enable"
do
  read -r unsupported_method unsupported_route <<<"${unsupported_request}"
  unsupported_status="$(curl -sS -o /dev/null -w '%{http_code}' -X "${unsupported_method}" \
    -H "${auth_header}" "${gateway_url}${unsupported_route}")"
  if [[ "${unsupported_status}" != "404" ]]; then
    echo "unsupported split route ${unsupported_route} returned ${unsupported_status}, expected 404" >&2
    exit 1
  fi
done

# The target process independently enforces ownership; directly sending the B
# workspace to A must return 421, while the correct owner serves it.
wrong_owner_status="$(internal_http_status agentteam-workspace-default /api/v1/workspaces/wsp_delivery_b/skills wsp_delivery_b)"
if [[ "${wrong_owner_status}" != "421" ]]; then
  echo "wrong workspace owner returned ${wrong_owner_status}, expected 421" >&2
  exit 1
fi

# Every MeshX family revalidates the resolved owner, including api/v1 routes
# that previously bypassed the target owner guard.
for route in \
  /api/v1/workspaces/wsp_delivery_b/mcp \
  /api/v1/workspaces/wsp_delivery_b/repositories/code-wiki/jobs/job_1 \
  /api/v1/workspaces/wsp_delivery_b/agent-thread/session-channel
do
  wrong_owner_status="$(internal_http_status agentteam-workspace-default "${route}" wsp_delivery_b)"
  if [[ "${wrong_owner_status}" != "421" ]]; then
    echo "wrong workspace owner for ${route} returned ${wrong_owner_status}, expected 421" >&2
    exit 1
  fi
done

# Final central has no MeshX/AGS compatibility route or runtime dependency.
central_status="$(internal_http_status agentteam-central /api/v3/repos/delivery-a/repo)"
if [[ "${central_status}" != "404" ]]; then
  echo "final central still exposes AGS compatibility route (status ${central_status})" >&2
  exit 1
fi

# Gateway readiness covers every configured owner. Losing one group must not
# silently route its traffic to central or another workspace group.
workspace_b_container="$("${compose[@]}" ps -q --all agentteam-workspace-large-a)"
if [[ -z "${workspace_b_container}" ]]; then
  echo "delivery split workspace B container was not created" >&2
  exit 1
fi
"${compose[@]}" stop agentteam-workspace-large-a >/dev/null
if curl -fsS "${gateway_url}/readyz" >/dev/null 2>&1; then
  echo "delivery split gateway stayed ready after a workspace owner stopped" >&2
  exit 1
fi
# Deliberately use the old public URL here to verify the Gateway compatibility
# rewrite does not fall back to another owner when placement is unavailable.
if curl -fsS -H "X-AgentTeam-Route-Workspace-Id: wsp_delivery_b" \
  "${gateway_url}/api/v1/skills" >/dev/null 2>&1; then
  echo "delivery split gateway fell back after the resolved workspace owner stopped" >&2
  exit 1
fi
# Start the exact stopped container so Compose does not rerun one-shot schema
# or repository fixtures while the other workspace owner remains live.
docker start "${workspace_b_container}" >/dev/null

for _ in $(seq 1 40); do
  if curl -fsS "${gateway_url}/readyz" >/dev/null; then
    echo "delivery split compose smoke passed"
    exit 0
  fi
  sleep 2
done

echo "delivery split gateway did not recover after workspace owner restart" >&2
exit 1
