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

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)"
cd "${root}"

old_workspace_url='["`]/api/(llm|v1/(agent-thread/session-channel|kb|memory|skills|roles(:from-agent)?|repository-(owners|names|access-tokens)|internal/repository-access-tokens|personal-access-tokens|automations|mcp|repositories))([/?"`]|$)'
spec_old_workspace_url='/api/(llm|v1/(agent-thread/session-channel|kb|memory|skills|roles(:from-agent)?|repository-(owners|names|access-tokens)|internal/repository-access-tokens|personal-access-tokens|automations|mcp|repositories))([/?"`[:space:]<>()\[\]{},.;:]|$)'

# Living specs must not present a native MeshX suffix or legacy LLM route as a
# current public URL. The canonicalization spec owns the old-to-new inventory;
# other specs may mention an old form only when the same line classifies it as
# a private in-process target or an explicit compatibility rewrite.
spec_matches="$(rg -n --pcre2 "${spec_old_workspace_url}" specs \
  --glob '*.md' \
  --glob '!specs/deployment/workspace-url-canonicalization.md' | \
  rg -v 'private in-process target|[Cc]ompatibility rewrite' || true)"
if [[ -n "${spec_matches}" ]]; then
  echo "a living spec still presents an unscoped workspace URL as current:" >&2
  echo "${spec_matches}" >&2
  exit 1
fi

# The retired Agent Runtime URL is not a supported compatibility entry point.
# Living specs may name it only when the same line marks the route as retired,
# unsupported, or an explicit 404 boundary.
agent_runtime_spec_matches="$(rg -n --pcre2 '(?<![[:alnum:]_.-])/internal/agent/' specs \
  --glob '*.md' \
  --glob '!specs/deployment/workspace-url-canonicalization.md' | \
  rg -v 'Retired|retired|Unsupported|unsupported|404' || true)"
if [[ -n "${agent_runtime_spec_matches}" ]]; then
  echo "a living spec still presents the retired Agent Runtime URL as supported:" >&2
  echo "${agent_runtime_spec_matches}" >&2
  exit 1
fi

check_public_callers() {
  local label="$1"
  shift
  local matches
  matches="$(rg -n --pcre2 "${old_workspace_url}" "$@" \
    --glob '!**/*.test.*' \
    --glob '!**/*_test.go' \
    --glob '!**/testdata/**' || true)"
  if [[ -n "${matches}" ]]; then
    echo "${label} still constructs an unscoped public workspace URL:" >&2
    echo "${matches}" >&2
    exit 1
  fi
}

# MeshX leaf API modules keep native /api/v1/... suffixes only as inputs to the
# canonical workspace transport, so scan their callers rather than those
# private suffix builders.
check_public_callers "Web caller" web/src --glob '!**/meshx/api/**'
check_public_callers "Web E2E caller" web/e2e
check_public_callers "Miniapp caller" miniapp

# Workspace-owned API leaves may keep native suffix literals as private inputs
# to the shared canonical transport, but must not bypass that owner with raw
# fetch/request primitives. The raw MeshX transport also rejects these native
# workspace families at runtime; its unit test covers that fail-closed seam.
workspace_api_files=()
while IFS= read -r file; do
  workspace_api_files+=("${file}")
done < <(rg -l --pcre2 "${old_workspace_url}" web/src/meshx/api \
  --glob '!**/*.test.*' --glob '!**/client.ts')
if ((${#workspace_api_files[@]} > 0)); then
  if rg -n --pcre2 '\b(requestJSON|requestWithAuth|fetch)\s*(?:<[^;]+>)?\(' \
    "${workspace_api_files[@]}"; then
    echo "a workspace-owned Web API leaf bypasses the canonical transport owner" >&2
    exit 1
  fi
fi

# Desktop has two deliberate exceptions: gateway.go carries the bounded old-Web
# header compatibility inventory, and kb_handler.go carries a private MeshX
# target inside the canonical Agent Runtime envelope. Provisioning providers
# pass native suffixes to PostAgentBatchAccess, which owns canonicalization.
check_public_callers "Desktop caller" desktop/internal \
  --glob '!**/api/gateway.go' \
  --glob '!**/api/mcp/kb_handler.go' \
  --glob '!**/provisioning/{asset,kb,memory,skill}/**'

if rg -n --pcre2 '["`]/internal/agent/' desktop miniapp web/src \
  --glob '!**/*.test.*' --glob '!**/*_test.go' --glob '!**/testdata/**'; then
  echo "a client still constructs the retired Agent Runtime URL" >&2
  exit 1
fi

grep -Fq '"/api/v1/workspaces/" + workspaceID + "/llm/v1"' \
  internal/store/agent/runtime_types.go
grep -Fq 'endpointPath = "/api/v1/workspaces/" + url.PathEscape(workspaceID)' \
  desktop/internal/provisioning/asset/provider.go
grep -Fq 'path="/api/v1/workspaces/$WORKSPACE_ID${path#/api/v1}"' \
  scripts/meshx-actions-runner-smoke.sh

echo "workspace URL caller guardrail passed"
