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

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

require_rg() {
  if ! command -v rg >/dev/null 2>&1; then
    echo "error: ripgrep (rg) is required" >&2
    exit 127
  fi
}

require_python3() {
  if ! command -v python3 >/dev/null 2>&1; then
    echo "error: python3 is required" >&2
    exit 127
  fi
}

count_go_refs() {
  local pattern="$1"
  local matches
  matches="$(rg "${pattern}" --glob '*.go' . 2>/dev/null || true)"
  if [[ -z "${matches}" ]]; then
    echo 0
    return
  fi
  printf '%s\n' "${matches}" | wc -l | tr -d '[:space:]'
}

count_refs() {
  local pattern="$1"
  shift
  local matches
  matches="$(rg "${pattern}" "$@" 2>/dev/null || true)"
  if [[ -z "${matches}" ]]; then
    echo 0
    return
  fi
  printf '%s\n' "${matches}" | wc -l | tr -d '[:space:]'
}

count_root_store_methods() {
  local files=()
  while IFS= read -r -d '' file; do
    files+=("${file}")
  done < <(find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \(store \*Store\) ' "${files[@]}"
}

count_root_store_exported_methods() {
  local files=()
  while IFS= read -r -d '' file; do
    files+=("${file}")
  done < <(find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \([^)]*\*Store\) [A-Z][A-Za-z0-9_]*\(' "${files[@]}"
}

count_root_store_files() {
  find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' | wc -l | tr -d '[:space:]'
}

count_root_store_test_files() {
  find internal/store -maxdepth 1 -type f -name '*_test.go' | wc -l | tr -d '[:space:]'
}

count_unapproved_root_store_files() {
  find internal/store -maxdepth 1 -type f -name '*.go' \
    ! -name 'graph.go' \
    ! -name 'organization_bridge.go' \
    ! -name 'graph_test.go' | wc -l | tr -d '[:space:]'
}

count_root_store_graph_methods() {
  local files=()
  while IFS= read -r -d '' file; do
    files+=("${file}")
  done < <(find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \([^)]*\*?Graph\) ' "${files[@]}"
}

count_root_store_methods_in_files() {
  local files=()
  local file
  for file in "$@"; do
    if [[ -f "${file}" ]]; then
      files+=("${file}")
    fi
  done
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \(store \*Store\) ' "${files[@]}"
}

count_root_store_type_aliases() {
  local files=()
  while IFS= read -r -d '' file; do
    files+=("${file}")
  done < <(find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  awk '
    /^type[[:space:]]*\(/ { in_type = 1; next }
    in_type && /^\)/ { in_type = 0; next }
    in_type && /^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/ { count++ }
    /^type[[:space:]]+[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/ { count++ }
    END { print count + 0 }
  ' "${files[@]}"
}

count_type_aliases_in_files() {
  local files=("$@")
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  awk '
    /^type[[:space:]]*\(/ { in_type = 1; next }
    in_type && /^\)/ { in_type = 0; next }
    in_type && /^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/ { count++ }
    /^type[[:space:]]+[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/ { count++ }
    END { print count + 0 }
  ' "${files[@]}"
}

storefacade_non_test_files() {
  { find internal/service/storefacade -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print 2>/dev/null || true; } | sort
}

storefacade_test_files() {
  { find internal/service/storefacade -maxdepth 1 -type f -name '*_test.go' -print 2>/dev/null || true; } | sort
}

count_storefacade_non_test_files() {
  storefacade_non_test_files | wc -l | tr -d '[:space:]'
}

count_storefacade_test_files() {
  storefacade_test_files | wc -l | tr -d '[:space:]'
}

count_storefacade_store_methods() {
  local files=()
  while IFS= read -r file; do
    files+=("${file}")
  done < <(storefacade_non_test_files)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \([^)]*\*Store\) ' "${files[@]}"
}

count_storefacade_exported_store_methods() {
  local files=()
  while IFS= read -r file; do
    files+=("${file}")
  done < <(storefacade_non_test_files)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \([^)]*\*Store\) [A-Z][A-Za-z0-9_]*\(' "${files[@]}"
}

count_storefacade_app_port_methods() {
  local files=()
  while IFS= read -r file; do
    files+=("${file}")
  done < <(storefacade_non_test_files)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_refs '^func \([^)]*\*AppPort\) ' "${files[@]}"
}

count_storefacade_type_aliases() {
  local files=()
  while IFS= read -r file; do
    files+=("${file}")
  done < <(storefacade_non_test_files)
  if (( ${#files[@]} == 0 )); then
    echo 0
    return
  fi
  count_type_aliases_in_files "${files[@]}"
}

approved_service_dirs=(
  internal/service/actionsrunner
  internal/service/agentruntime
  internal/service/agentapproval
  internal/service/agentmemory
  internal/service/app/installation
  internal/service/approvalapp
  internal/service/authflow
  internal/service/appruntime
  internal/service/automation
  internal/service/automation/runtime
  internal/service/channeladmin
  internal/service/collaborator
  internal/service/content/migration
  internal/service/content
  internal/service/extensionflow
  internal/service/kb
  internal/service/knowledgeqa
  internal/service/knowledgeqa/runtime
  internal/service/maas
  internal/service/memory
  internal/service/message
  internal/service/organization
  internal/service/providertokens
  internal/service/prwatch
  internal/service/repocache
  internal/service/repotokens
  internal/service/resource/access
  internal/service/resource/activity
  internal/service/resource/repos
  internal/service/role/agents
  internal/service/role/execution
  internal/service/roles
  internal/service/serviceaccount
  internal/service/serviceevent
  internal/service/skills
  internal/service/team/roleproposal
  internal/service/teamworkflow
  internal/service/workspace
)

is_approved_service_dir() {
  local dir="${1%/}"
  local approved
  for approved in "${approved_service_dirs[@]}"; do
    if [[ "${dir}" == "${approved}" ]]; then
      return 0
    fi
  done
  return 1
}

count_existing_files() {
  local count=0
  local file
  for file in "$@"; do
    if [[ -f "${file}" ]]; then
      count=$((count + 1))
    fi
  done
  echo "${count}"
}

production_root_store_import_files() {
  rg -l '"github\.com/tidbcloud/AgentTeam/internal/store"' . --glob '*.go' --glob '!*_test.go' 2>/dev/null \
    | sed 's#^\./##' \
    | grep -v '^internal/store/' \
    | sort \
    || true
}

check_production_root_store_import_allowlist() {
  local files
  files="$(production_root_store_import_files)"
  local actual
  if [[ -z "${files}" ]]; then
    actual=0
  else
    actual="$(printf '%s\n' "${files}" | wc -l | tr -d '[:space:]')"
  fi
  check_max "production root internal/store import files" "${actual}" "${STORE_REFACTOR_MAX_PRODUCTION_ROOT_STORE_IMPORT_FILES:-3}"

  local failed=0
  local file
  while IFS= read -r file; do
    [[ -z "${file}" ]] && continue
    case "${file}" in
      internal/server/server.go)
        ;;
      internal/server/infra.go)
        # Server infrastructure remains a composition boundary while domain
        # ports are still being split out of the root store graph.
        ;;
      cmd/agentteam-e2e-server/main.go)
        # The e2e server executable is a composition root like production
        # server wiring; it may construct the root store graph but must not
        # pass it into API handlers as a broad dependency.
        ;;
      internal/api/testkit/store.go)
        # Non-test fixture source used by API integration tests. This is not a
        # production route path; it remains the one API fixture composition
        # boundary that constructs the root graph.
        ;;
      *)
        echo "error: production root internal/store import outside allowlist: ${file}" >&2
        failed=1
        ;;
    esac
  done <<< "${files}"
  if (( failed )); then
    exit 1
  fi
  echo "production root internal/store import allowlist: ok"
}

storefacade_prod_import_files() {
  rg -l '"github\.com/tidbcloud/AgentTeam/internal/service/storefacade"' . \
    --glob '*.go' \
    --glob '!*_test.go' \
    --glob '!internal/service/storefacade/**' \
    --glob '!x/**' \
    --glob '!.worktrees/**' 2>/dev/null \
    | sed 's#^\./##' \
    | sort \
    || true
}

storefacade_test_import_files() {
  rg -l '"github\.com/tidbcloud/AgentTeam/internal/service/storefacade"' . \
    --glob '*_test.go' \
    --glob '!internal/service/storefacade/**' \
    --glob '!x/**' \
    --glob '!.worktrees/**' 2>/dev/null \
    | sed 's#^\./##' \
    | sort \
    || true
}

count_non_storefacade_service_dirs() {
  { find internal/service -mindepth 1 -type d ! -name 'storefacade' -print 2>/dev/null || true; } \
    | while IFS= read -r dir; do
      # Service directories with Go files are approved by the domain allowlist.
      # Wrapper-only grouping directories remain allowed only while they contain no Go files.
      if find "${dir}" -maxdepth 1 -type f -name '*.go' | grep -q .; then
        if ! is_approved_service_dir "${dir}"; then
          printf '%s\n' "${dir}"
        fi
      fi
    done \
    | wc -l \
    | tr -d '[:space:]'
}

count_non_storefacade_service_go_files() {
  { find internal/service -mindepth 2 -type f -name '*.go' ! -path 'internal/service/storefacade/*' -print 2>/dev/null || true; } \
    | while IFS= read -r file; do
      dir="${file%/*}"
      if ! is_approved_service_dir "${dir}"; then
        printf '%s\n' "${file}"
      fi
    done \
    | wc -l \
    | tr -d '[:space:]'
}

non_storefacade_service_import_files() {
  local allowed_imports
  allowed_imports="storefacade"
  local dir
  for dir in "${approved_service_dirs[@]}"; do
    allowed_imports="${allowed_imports}|${dir#internal/service/}"
  done
  rg -l -P "\"go\\.pingcap\\.cn/agentteam/internal/service/(?!(${allowed_imports})\")" . \
    --glob '*.go' \
    --glob '!x/**' \
    --glob '!vendor/**' \
    --glob '!.worktrees/**' 2>/dev/null \
    | sed 's#^\./##' \
    | sort \
    || true
}

count_lines_or_zero() {
  local lines="$1"
  if [[ -z "${lines}" ]]; then
    echo 0
    return
  fi
  printf '%s\n' "${lines}" | wc -l | tr -d '[:space:]'
}

root_store_method_file_limit() {
  # Final zero-ratchet: no exact root internal/store files may retain
  # package-level *Store method allowance.
  echo 0
}
check_root_store_method_file_ratchet() {
  local failed=0
  local file
  while IFS= read -r -d '' file; do
    local actual
    actual="$(count_refs '^func \([^)]*\*Store\) ' "${file}")"
    local max
    max="$(root_store_method_file_limit "${file}")"
    if (( actual > max )); then
      echo "error: root Store method count for ${file} increased above allowlist (${actual} > ${max})" >&2
      failed=1
    fi
  done < <(find internal/store -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( failed )); then
    exit 1
  fi
  echo "root Store method per-file ratchet: ok"
}

check_max() {
  local label="$1"
  local actual="$2"
  local max="$3"
  printf '%s: %s (max %s)\n' "${label}" "${actual}" "${max}"
  if (( actual > max )); then
    echo "error: ${label} increased above allowed baseline (${actual} > ${max})" >&2
    exit 1
  fi
}

check_store_forbidden_imports() {
  local failed=0
  local import_path
  for import_path in \
    'github\.com/tidbcloud/AgentTeam/internal/api' \
    'github\.com/tidbcloud/AgentTeam/internal/router' \
    'github\.com/tidbcloud/AgentTeam/internal/server' \
    'github\.com/tidbcloud/AgentTeam/cmd' \
    'github\.com/tidbcloud/AgentTeam/web' \
    'github\.com/tidbcloud/AgentTeam/desktop'; do
    local matches
    matches="$(rg "\"${import_path}" internal/store --glob '*.go' 2>/dev/null || true)"
    if [[ -n "${matches}" ]]; then
      echo "error: internal/store must not import ${import_path//\\./.}:" >&2
      printf '%s\n' "${matches}" >&2
      failed=1
    fi
  done
  if (( failed )); then
    exit 1
  fi
  echo "internal/store forbidden upward imports: 0"
}

check_store_legacy_usecase_imports() {
  local files
  files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/usecase' internal/store --glob '*.go' 2>/dev/null | sort || true)"
  local actual
  actual="$(count_lines_or_zero "${files}")"
  local max="${STORE_REFACTOR_MAX_STORE_LEGACY_USECASE_IMPORT_FILES:-0}"
  printf 'internal/store -> legacy internal/usecase import files: %s (max %s)\n' "${actual}" "${max}"
  if (( actual > max )); then
    echo "error: internal/store must not import legacy internal/usecase:" >&2
    rg '"github\.com/tidbcloud/AgentTeam/internal/usecase' internal/store --glob '*.go' >&2 || true
    exit 1
  fi
  echo "internal/store -> legacy internal/usecase imports: 0"
}

check_store_domain_root_imports() {
  local files=()
  while IFS= read -r -d '' file; do
    files+=("${file}")
  done < <(find internal/store -mindepth 2 -type f -name '*.go' ! -name '*_test.go' -print0)
  if (( ${#files[@]} == 0 )); then
    echo "internal/store domain production root imports: 0 (forbidden)"
    return
  fi
  local matches
  matches="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/store"' "${files[@]}" 2>/dev/null | sort || true)"
  local actual
  actual="$(count_lines_or_zero "${matches}")"
  local max="${STORE_REFACTOR_MAX_STORE_DOMAIN_ROOT_IMPORT_FILES:-0}"
  printf 'internal/store domain production root import files: %s (max %s)\n' "${actual}" "${max}"
  if (( actual > max )); then
    echo "error: internal/store/{domain} production code must not import root internal/store:" >&2
    rg '"github\.com/tidbcloud/AgentTeam/internal/store"' "${files[@]}" >&2 || true
    exit 1
  fi
  local failed=0
  local file
  while IFS= read -r file; do
    [[ -z "${file}" ]] && continue
    case "${file}" in
      internal/store/testkit/store.go)
        # Store test fixture wiring is non-test source but not a production
        # domain implementation.
        ;;
      *)
        echo "error: internal/store domain root import outside allowlist: ${file}" >&2
        failed=1
        ;;
    esac
  done <<< "${matches}"
  if (( failed )); then
    exit 1
  fi
  echo "internal/store domain production root import allowlist: ok"
}

check_approved_service_forbidden_imports() {
  local files=()
  local dir
  for dir in "${approved_service_dirs[@]}"; do
    while IFS= read -r file; do
      files+=("${file}")
    done < <(find "${dir}" -maxdepth 1 -type f -name '*.go' ! -name '*_test.go' -print 2>/dev/null || true)
  done
  if (( ${#files[@]} == 0 )); then
    echo "approved internal/service api imports: 0 (forbidden)"
    return
  fi
  local matches
  matches="$(rg '"github\.com/tidbcloud/AgentTeam/internal/api' "${files[@]}" 2>/dev/null || true)"
  if [[ -n "${matches}" ]]; then
    echo "error: approved internal/service packages must not import internal/api:" >&2
    printf '%s\n' "${matches}" >&2
    exit 1
  fi
  echo "approved internal/service api imports: 0 (forbidden)"
}

check_obvious_noop_import_aliases() {
  require_python3
  local output
  output="$(python3 - <<'PY'
import pathlib
import re

root = pathlib.Path(".")
matches = []
for path in root.rglob("*.go"):
    rel = path.as_posix()
    if (
        rel.startswith(".git/")
        or rel.startswith(".worktrees/")
        or rel.startswith("x/")
        or rel.startswith("vendor/")
        or "/.worktrees/" in rel
        or "/x/" in rel
        or "/vendor/" in rel
    ):
        continue
    in_block = False
    for lineno, line in enumerate(path.read_text(errors="ignore").splitlines(), 1):
        stripped = line.strip()
        if stripped.startswith("import ("):
            in_block = True
            continue
        match = None
        if in_block:
            if stripped.startswith(")"):
                in_block = False
                continue
            match = re.match(r'([A-Za-z_][A-Za-z0-9_]*)\s+"([^"]+)"', stripped)
        else:
            match = re.match(r'import\s+([A-Za-z_][A-Za-z0-9_]*)\s+"([^"]+)"', stripped)
        if not match or match.group(1) in {"_", "."}:
            continue
        alias, import_path = match.groups()
        path_base = import_path.rsplit("/", 1)[-1].replace("-", "_")
        if alias == path_base:
            matches.append(f"{rel}:{lineno}: {alias} {import_path}")

print(len(matches))
for item in matches:
    print(item)
PY
)"
  local actual
  actual="$(printf '%s\n' "${output}" | sed -n '1p')"
  local max="${STORE_REFACTOR_MAX_OBVIOUS_NOOP_IMPORT_ALIASES:-0}"
  printf 'obvious no-op import aliases: %s (max %s)\n' "${actual}" "${max}"
  if (( actual > max )); then
    echo "error: obvious no-op import aliases increased above allowed baseline (${actual} > ${max}):" >&2
    printf '%s\n' "${output}" | sed '1d' >&2
    exit 1
  fi
}

require_rg

phase10_rewind_files=(
  internal/store/rewind/store.go
  internal/store/rewind/recovery.go
)
phase10_reaction_files=(
  internal/store/reaction/store.go
)
phase10_review_files=(
  internal/store/review/review_context.go
)
phase10_agent_ops_files=(
  internal/store/review/output_target.go
  internal/store/review/agent_review.go
  internal/store/agentops/runtime_events.go
)
phase10_app_ecosystem_files=(
  internal/store/app/app_channels.go
  internal/store/app/app_connections.go
  internal/store/app/app_credentials.go
  internal/store/app/app_deliveries.go
  internal/store/app/app_installations.go
  internal/store/app/app_invites.go
  internal/store/app/app_manifest_store.go
  internal/store/app/app_service_messages.go
  internal/store/app/app_message_delivery_preview.go
  internal/store/app/app_messages.go
  internal/store/app/app_runtime.go
  internal/store/app/app_service_authors.go
  internal/store/app/app_workspaces.go
  internal/store/app/app_usage.go
  internal/store/app/app_webhooks.go
  internal/store/app/app_webhooks_gitea.go
  internal/store/app/app_ws.go
  internal/store/app/apps.go
  internal/store/app/github_app_api.go
  internal/store/app/provider.go
  internal/store/app/provider_secrets.go
  internal/store/app/provider_watch_runtime.go
)
while IFS= read -r file; do
  phase10_app_ecosystem_files+=("${file}")
done < <(find internal/store/app -maxdepth 1 -type f \
  \( -name 'service_event*.go' -o -name 'pull_request_subscription*.go' \) \
  ! -name '*_test.go' -print | sort)
root_alias_facade_files=(
  internal/store/type_aliases.go
  internal/store/app_facade.go
  internal/store/facade_compat.go
  internal/store/app_port.go
  internal/store/app_domain_compat.go
  internal/store/agent_runtime_facade.go
)

pkg_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/pkg/')"
legacy_coredb_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/store/coredb')"
legacy_testkit_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/testkit\"')"
legacy_meshxshare_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/meshxshare')"
legacy_meshxshare_go_files="$({ find internal/meshxshare -type f -name '*.go' 2>/dev/null || true; } | wc -l | tr -d '[:space:]')"
legacy_bootstrap_storegraph_imports="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/bootstrap/storegraph' --no-ignore --glob '*.go' --glob '!x/**' --glob '!vendor/**' --glob '!.worktrees/**' .)"
non_storefacade_service_dirs="$(count_non_storefacade_service_dirs)"
non_storefacade_service_go_files="$(count_non_storefacade_service_go_files)"
non_storefacade_service_import_files_value="$(non_storefacade_service_import_files)"
non_storefacade_service_import_count="$(count_lines_or_zero "${non_storefacade_service_import_files_value}")"
store_coredb_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/coredb\"')"
store_coredbtest_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/coredb/testkit\"')"
store_imports="$(count_go_refs 'github\.com/tidbcloud/AgentTeam/internal/store')"
api_handler_store_refs="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store' internal/api --glob 'api_*.go' --glob '!*_test.go')"
api_dto_converter_store_refs="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store' internal/api/dto/converter --glob '*.go')"
api_store_model_refs="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store/model' internal/api --glob '*.go')"
api_root_store_imports="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store\"' internal/api --glob '*.go' --glob '!*_test.go')"
api_prod_storefacade_import_files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/service/storefacade"' internal/api --glob '*.go' --glob '!*_test.go' 2>/dev/null | sort || true)"
api_prod_storefacade_import_count="$(count_lines_or_zero "${api_prod_storefacade_import_files}")"
api_app_prod_storefacade_import_files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/service/storefacade"' internal/api/app --glob '*.go' --glob '!*_test.go' 2>/dev/null | sort || true)"
api_app_prod_storefacade_import_count="$(count_lines_or_zero "${api_app_prod_storefacade_import_files}")"
api_message_prod_storefacade_import_files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/service/storefacade"' internal/api/message --glob '*.go' --glob '!*_test.go' 2>/dev/null | sort || true)"
api_message_prod_storefacade_import_count="$(count_lines_or_zero "${api_message_prod_storefacade_import_files}")"
api_prod_bootstrap_storegraph_import_files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/bootstrap/storegraph"' internal/api --glob '*.go' --glob '!*_test.go' 2>/dev/null | sort || true)"
api_prod_bootstrap_storegraph_import_count="$(count_lines_or_zero "${api_prod_bootstrap_storegraph_import_files}")"
api_app_prod_bootstrap_storegraph_import_files="$(rg -l '"github\.com/tidbcloud/AgentTeam/internal/bootstrap/storegraph"' internal/api/app --glob '*.go' --glob '!*_test.go' 2>/dev/null | sort || true)"
api_app_prod_bootstrap_storegraph_import_count="$(count_lines_or_zero "${api_app_prod_bootstrap_storegraph_import_files}")"
api_app_root_store_imports="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store"' internal/api/app --glob '*.go' --glob '!*_test.go')"
api_message_root_store_imports="$(count_refs 'github\.com/tidbcloud/AgentTeam/internal/store"' internal/api/message --glob '*.go' --glob '!*_test.go')"
root_store_files="$(count_root_store_files)"
root_store_test_files="$(count_root_store_test_files)"
root_store_unapproved_files="$(count_unapproved_root_store_files)"
root_store_methods="$(count_root_store_methods)"
root_store_exported_methods="$(count_root_store_exported_methods)"
root_store_graph_methods="$(count_root_store_graph_methods)"
root_store_type_aliases="$(count_root_store_type_aliases)"
root_alias_facade_files_count="$(count_existing_files "${root_alias_facade_files[@]}")"
storefacade_non_test_file_count="$(count_storefacade_non_test_files)"
storefacade_test_file_count="$(count_storefacade_test_files)"
storefacade_store_methods="$(count_storefacade_store_methods)"
storefacade_exported_store_methods="$(count_storefacade_exported_store_methods)"
storefacade_app_port_methods="$(count_storefacade_app_port_methods)"
storefacade_type_aliases="$(count_storefacade_type_aliases)"
storefacade_prod_import_files_value="$(storefacade_prod_import_files)"
storefacade_prod_import_count="$(count_lines_or_zero "${storefacade_prod_import_files_value}")"
storefacade_test_import_files_value="$(storefacade_test_import_files)"
storefacade_test_import_count="$(count_lines_or_zero "${storefacade_test_import_files_value}")"
phase10_rewind_methods="$(count_root_store_methods_in_files "${phase10_rewind_files[@]}")"
phase10_reaction_methods="$(count_root_store_methods_in_files "${phase10_reaction_files[@]}")"
phase10_review_methods="$(count_root_store_methods_in_files "${phase10_review_files[@]}")"
phase10_agent_ops_methods="$(count_root_store_methods_in_files "${phase10_agent_ops_files[@]}")"
phase10_app_ecosystem_methods="$(count_root_store_methods_in_files "${phase10_app_ecosystem_files[@]}")"

# Hard dependency-direction gate: the store package must NEVER import the API,
# router, server, cmd, web, or desktop packages. This runs BEFORE the ratchet
# baselines so a store->api import cannot hide behind an earlier baseline
# failure.
check_store_forbidden_imports

check_max "github.com/tidbcloud/AgentTeam/pkg imports" "${pkg_imports}" "${STORE_REFACTOR_MAX_PKG_IMPORTS:-0}"
check_max "legacy github.com/tidbcloud/AgentTeam/internal/store/coredb imports" "${legacy_coredb_imports}" "${STORE_REFACTOR_MAX_LEGACY_COREDB_IMPORTS:-${STORE_REFACTOR_MAX_COREDB_IMPORTS:-0}}"
check_max "legacy github.com/tidbcloud/AgentTeam/internal/testkit imports" "${legacy_testkit_imports}" "${STORE_REFACTOR_MAX_LEGACY_TESTKIT_IMPORTS:-0}"
check_max "legacy github.com/tidbcloud/AgentTeam/internal/meshxshare imports" "${legacy_meshxshare_imports}" "${STORE_REFACTOR_MAX_LEGACY_MESHXSHARE_IMPORTS:-0}"
check_max "legacy internal/meshxshare Go files" "${legacy_meshxshare_go_files}" "${STORE_REFACTOR_MAX_LEGACY_MESHXSHARE_GO_FILES:-0}"
check_max "legacy internal/bootstrap/storegraph imports" "${legacy_bootstrap_storegraph_imports}" "${STORE_REFACTOR_MAX_LEGACY_BOOTSTRAP_STOREGRAPH_IMPORTS:-0}"
printf 'approved internal/service orchestration packages: %s\n' "${approved_service_dirs[*]}"
check_max "unapproved non-storefacade internal/service dirs" "${non_storefacade_service_dirs}" "${STORE_REFACTOR_MAX_NON_STOREFACADE_SERVICE_DIRS:-0}"
check_max "unapproved non-storefacade internal/service Go files" "${non_storefacade_service_go_files}" "${STORE_REFACTOR_MAX_NON_STOREFACADE_SERVICE_GO_FILES:-0}"
check_max "unapproved non-storefacade internal/service import files" "${non_storefacade_service_import_count}" "${STORE_REFACTOR_MAX_NON_STOREFACADE_SERVICE_IMPORT_FILES:-0}"
check_max "internal/api api_*.go store refs (target 0)" "${api_handler_store_refs}" "${STORE_REFACTOR_MAX_API_HANDLER_STORE_REFS:-0}"
bash guardrails/store/check-api-dto-store-boundary.sh
printf 'internal/api/dto/converter store refs: %s (informational — converter is allowed to import store)\n' "${api_dto_converter_store_refs}"
check_max "internal/api store/model refs" "${api_store_model_refs}" "${STORE_REFACTOR_MAX_API_STORE_MODEL_REFS:-0}"
check_max "internal/api production root store imports (target 0)" "${api_root_store_imports}" "${STORE_REFACTOR_MAX_API_ROOT_STORE_IMPORTS:-0}"
check_max "internal/api production storefacade import files" "${api_prod_storefacade_import_count}" "${STORE_REFACTOR_MAX_API_PROD_STOREFACADE_IMPORT_FILES:-0}"
check_max "internal/api/app production storefacade import files" "${api_app_prod_storefacade_import_count}" "${STORE_REFACTOR_MAX_API_APP_PROD_STOREFACADE_IMPORT_FILES:-0}"
check_max "internal/api/message production storefacade import files" "${api_message_prod_storefacade_import_count}" "${STORE_REFACTOR_MAX_API_MESSAGE_PROD_STOREFACADE_IMPORT_FILES:-0}"
check_max "internal/api production bootstrap/storegraph import files" "${api_prod_bootstrap_storegraph_import_count}" "${STORE_REFACTOR_MAX_API_PROD_STOREGRAPH_IMPORT_FILES:-0}"
check_max "internal/api/app production bootstrap/storegraph import files" "${api_app_prod_bootstrap_storegraph_import_count}" "${STORE_REFACTOR_MAX_API_APP_PROD_STOREGRAPH_IMPORT_FILES:-0}"
check_max "internal/api/app production root store imports" "${api_app_root_store_imports}" "${STORE_REFACTOR_MAX_API_APP_ROOT_STORE_IMPORTS:-0}"
check_max "internal/api/message production root store imports" "${api_message_root_store_imports}" "${STORE_REFACTOR_MAX_API_MESSAGE_ROOT_STORE_IMPORTS:-0}"
# Root internal/store is limited to graph initialization/composition files.
check_max "root internal/store non-test Go files" "${root_store_files}" "${STORE_REFACTOR_MAX_ROOT_STORE_FILES:-1}"
check_max "root internal/store test files" "${root_store_test_files}" "${STORE_REFACTOR_MAX_ROOT_STORE_TEST_FILES:-1}"
check_max "root internal/store unapproved Go files" "${root_store_unapproved_files}" "${STORE_REFACTOR_MAX_ROOT_STORE_UNAPPROVED_FILES:-0}"
check_max "root internal/store Store methods" "${root_store_methods}" "${STORE_REFACTOR_MAX_ROOT_STORE_METHODS:-0}"
check_max "root internal/store exported *Store methods" "${root_store_exported_methods}" "${STORE_REFACTOR_MAX_ROOT_EXPORTED_STORE_METHODS:-0}"
check_max "root internal/store Graph methods" "${root_store_graph_methods}" "${STORE_REFACTOR_MAX_ROOT_STORE_GRAPH_METHODS:-0}"
check_max "root internal/store type aliases" "${root_store_type_aliases}" "${STORE_REFACTOR_MAX_ROOT_STORE_TYPE_ALIASES:-0}"
check_max "root internal/store alias/facade files" "${root_alias_facade_files_count}" "${STORE_REFACTOR_MAX_ROOT_ALIAS_FACADE_FILES:-0}"
check_root_store_method_file_ratchet
check_max "storefacade non-test Go files" "${storefacade_non_test_file_count}" "${STORE_REFACTOR_MAX_STOREFACADE_FILES:-0}"
check_max "storefacade test files" "${storefacade_test_file_count}" "${STORE_REFACTOR_MAX_STOREFACADE_TEST_FILES:-0}"
check_max "storefacade Store methods" "${storefacade_store_methods}" "${STORE_REFACTOR_MAX_STOREFACADE_STORE_METHODS:-0}"
check_max "storefacade exported Store methods" "${storefacade_exported_store_methods}" "${STORE_REFACTOR_MAX_STOREFACADE_EXPORTED_STORE_METHODS:-0}"
check_max "storefacade AppPort methods" "${storefacade_app_port_methods}" "${STORE_REFACTOR_MAX_STOREFACADE_APPPORT_METHODS:-0}"
check_max "storefacade type aliases" "${storefacade_type_aliases}" "${STORE_REFACTOR_MAX_STOREFACADE_TYPE_ALIASES:-0}"
check_max "storefacade production import files" "${storefacade_prod_import_count}" "${STORE_REFACTOR_MAX_STOREFACADE_PROD_IMPORT_FILES:-0}"
check_max "storefacade test import files" "${storefacade_test_import_count}" "${STORE_REFACTOR_MAX_STOREFACADE_TEST_IMPORT_FILES:-0}"
check_obvious_noop_import_aliases
check_max "Phase 10.2 rewind root Store methods" "${phase10_rewind_methods}" "${STORE_REFACTOR_MAX_PHASE10_REWIND_METHODS:-0}"
check_max "Phase 10.3 reaction root Store methods" "${phase10_reaction_methods}" "${STORE_REFACTOR_MAX_PHASE10_REACTION_METHODS:-0}"
check_max "Phase 10.4 review root Store methods" "${phase10_review_methods}" "${STORE_REFACTOR_MAX_PHASE10_REVIEW_METHODS:-0}"
check_max "Phase 10.5 agent-ops root Store methods" "${phase10_agent_ops_methods}" "${STORE_REFACTOR_MAX_PHASE10_AGENT_OPS_METHODS:-0}"
check_max "Phase 10.6 app-ecosystem root Store methods" "${phase10_app_ecosystem_methods}" "${STORE_REFACTOR_MAX_PHASE10_APP_ECOSYSTEM_METHODS:-0}"
printf 'github.com/tidbcloud/AgentTeam/internal/coredb imports: %s (informational current surface)\n' "${store_coredb_imports}"
printf 'github.com/tidbcloud/AgentTeam/internal/coredb/testkit imports: %s (informational current surface)\n' "${store_coredbtest_imports}"
printf 'github.com/tidbcloud/AgentTeam/internal/store imports: %s (informational current surface)\n' "${store_imports}"
check_production_root_store_import_allowlist
check_store_legacy_usecase_imports
check_store_domain_root_imports
check_approved_service_forbidden_imports

# DTO converter dependency hard rules:
# 1. converter must NOT import any internal/api/{domain} package (dto itself is ok)
# 2. dto parent package must NOT import converter
converter_api_domain_refs="$(rg 'github\.com/tidbcloud/AgentTeam/internal/api/(agent|admin|app|auth|channel|daemon|kb|media|message|org|provider|public|realtime|role|task|testkit|trigger|webagent|workspace)' internal/api/dto/converter --glob '*.go' 2>/dev/null || true)"
if [[ -n "${converter_api_domain_refs}" ]]; then
  echo "error: internal/api/dto/converter must not import internal/api/{domain} packages:" >&2
  printf '%s\n' "${converter_api_domain_refs}" >&2
  exit 1
fi
echo "internal/api/dto/converter api-domain imports: 0 (forbidden)"

dto_converter_refs="$(rg 'github\.com/tidbcloud/AgentTeam/internal/api/dto/converter' internal/api/dto --glob '*.go' 2>/dev/null | grep -v '/converter/' || true)"
if [[ -n "${dto_converter_refs}" ]]; then
  echo "error: internal/api/dto parent must not import internal/api/dto/converter:" >&2
  printf '%s\n' "${dto_converter_refs}" >&2
  exit 1
fi
echo "internal/api/dto parent converter imports: 0 (forbidden)"
