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

ROOT="${AGENTTEAM_MESSAGE_GUARDRAIL_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
cd "$ROOT"

failures=0

fail() {
  printf 'FAIL: %s\n' "$1" >&2
  failures=$((failures + 1))
}

require_contains() {
  local file="$1"
  local pattern="$2"
  local message="$3"
  if [[ ! -f "$file" ]]; then
    fail "$message (missing $file)"
    return
  fi
  if ! rg -q -P -- "$pattern" "$file"; then
    fail "$message"
  fi
}

check_direct_scope_message_inserts() {
  local pattern='INSERT[[:space:]]+(?:IGNORE[[:space:]]+)?INTO[[:space:]]+`?scope_messages'
  local matches
  matches="$(rg -n -P \
    --glob '*.go' \
    --glob '!**/*_test.go' \
    -- "$pattern" internal cmd desktop scripts 2>/dev/null || true)"
  if [[ -z "$matches" ]]; then
    fail "canonical scope_messages insert helper must remain discoverable"
    return
  fi

  local unexpected
  unexpected="$(printf '%s\n' "$matches" | grep -v '^internal/store/message/scope_store.go:' || true)"
  if [[ -n "$unexpected" ]]; then
    fail "direct production scope_messages inserts must go through the canonical message store boundary"
    printf '%s\n' "$unexpected" >&2
  fi
}

check_retired_system_card_writes() {
  local matches
  matches="$(rg -n -P \
    --glob '*.go' \
    --glob '!**/*_test.go' \
    --glob '!internal/store/message/message_semantics.go' \
    --glob '!internal/store/message/message_types.go' \
    --glob '!internal/store/app/message_append.go' \
    -- 'SystemCardKind\(' internal cmd desktop scripts 2>/dev/null || true)"
  if [[ -n "$matches" ]]; then
    fail "production system.card.kind policy must stay inside semantics classification and the canonical append write gate"
    printf '%s\n' "$matches" >&2
  fi

  matches="$(rg -n -P \
    --glob '*.go' \
    --glob '!**/*_test.go' \
    -- '"card"[[:space:]]*:' internal/store/app internal/api internal/service 2>/dev/null || true)"
  if [[ -n "$matches" ]]; then
    fail "production app/api/service code must not construct metadata.card system-card payloads"
    printf '%s\n' "$matches" >&2
  fi
}

require_contains \
  internal/store/message/message_semantics.go \
  'func RegisteredMessageTypeSemantics\(' \
  "message semantics registry must expose registered message-type descriptors for coverage checks"

require_contains \
  internal/store/message/message_semantics.go \
  'func RegisteredSystemCardKindSemantics\(' \
  "message semantics registry must expose registered system-card descriptors for coverage checks"

require_contains \
  internal/store/message/message_semantics.go \
  'func IsPublicHumanCreateMessageTypeAllowed\(' \
  "message semantics registry must expose public human-create message-type policy"

require_contains \
  internal/api/message/api_messages.go \
  'IsPublicHumanCreateMessageTypeAllowed' \
  "public message create API must use the semantics guardrail for message types"

require_contains \
  internal/store/message/message_types_test.go \
  'TestHumanVisibleMessageTypeSQLMatchesMessageSemantics' \
  "Go and SQL human-visible semantics must have parity coverage"

require_contains \
  internal/store/message/message_types.go \
  'humanVisibleSQLHiddenMessageTypes\(' \
  "SQL human-visible hidden message types must be derived from message semantics"

require_contains \
  internal/store/message/message_types_test.go \
  'TestHumanVisibleMessageTypeSQLDerivesHiddenListsFromSemantics' \
  "descriptor-derived SQL hidden lists must have coverage"

if rg -q '\\$\.card\.kind' internal/store/message/message_types.go; then
  fail "SQL human-visible predicate must not keep retired system.card.kind read fallback"
fi

require_contains \
  internal/store/message/message_semantics.go \
  'ParticipationProfile' \
  "message participation policy must live in message semantics"

require_contains \
  internal/store/message/message_semantics.go \
  'AgentUnreadProfile' \
  "agent unread policy must live in message semantics"

require_contains \
  internal/store/message/message_types.go \
  'ParticipationExcludedMessageTypes' \
  "message participation exclusions must be derived from message semantics"

require_contains \
  internal/store/message/message_types.go \
  'AgentUnreadExcludedMessageTypes' \
  "agent unread exclusions must be derived from message semantics"

require_contains \
  internal/store/app/app_messages.go \
  'message.ParticipationExcludedMessageTypes()' \
  "participation stats must consume semantics-derived exclusions"

require_contains \
  internal/store/message/shared_query_builders.go \
  'AgentUnreadExcludedMessageTypes()' \
  "agent unread query must consume semantics-derived exclusions"

require_contains \
  internal/store/message/message_types_test.go \
  'TestMessageReadSurfaceProfilesPreserveCurrentPolicy' \
  "participation and agent-unread profiles must have coverage"

require_contains \
  internal/store/message/message_types_test.go \
  'TestEnvelopeMessageSemanticsStayAllowlisted' \
  "hidden/envelope message semantics must have allowlist coverage"

require_contains \
  internal/store/message/message_types_test.go \
  'TestNoRegisteredSystemCardSemanticsRemain' \
  "retired system-card read fallback must stay deleted"

require_contains \
  internal/store/app/message_append.go \
  'SystemCardKind\(options\.Metadata\) != ""' \
  "canonical message append must reject retired system.card.kind writes"

require_contains \
  internal/tidb/migrations/00156_system_card_kind_cleanup.sql \
  'DELETE FROM `scope_messages`' \
  "system-card cleanup migration must delete unsupported retired system cards"

# TiDB migration regression coverage moved to the dedicated coverage
# repository; this repo no longer ships the internal/tidb test suite.

require_contains \
  internal/store/app/app_messages.go \
  'MessageRenderSticker' \
  "app message prepare must use semantics render policy for sticker constraints"

require_contains \
  internal/store/app/app_messages.go \
  'MessageDeliveryExplicit' \
  "app message prepare must use semantics delivery profile for explicit routing"

check_direct_scope_message_inserts
check_retired_system_card_writes

if (( failures > 0 )); then
  exit 1
fi

printf 'message semantics guardrails OK\n'
