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

# Jenkins restores the AgentTeam checkout inside an outer ci-ops Git worktree, so
# `git rev-parse --show-toplevel` can resolve to the unrelated parent checkout.
# Anchor the default root to this tracked script instead; the override remains
# available for isolated negative probes.
ROOT="${AGENTTEAM_MESSAGE_POST_COMMIT_PRODUCERS_GUARDRAIL_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)}"
cd "$ROOT"

failures=0

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

require_file() {
  local file="$1"
  [[ -f "$file" ]] || fail "missing $file"
}

require_contains() {
  local file="$1"
  local needle="$2"
  local label="${3:-$file must contain $needle}"
  if [[ ! -f "$file" ]]; then
    fail "missing $file"
    return
  fi
  if ! grep -Fq -- "$needle" "$file"; then
    fail "$label"
  fi
}

print_inventory() {
  if [[ -f guardrails/message/post-commit-producer-inventory.md ]]; then
    cat guardrails/message/post-commit-producer-inventory.md
  else
    printf 'No autonomous post-commit producer inventory found.\n'
  fi

  printf '\n## Current production contracted App-service append/finalize calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'appendAppServiceMessageWithOptions\(|finalizeAppServiceMessageTx\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production raw message append lifecycle calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'appendMessageToThread(Result)?\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production privateagent raw writer calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'writeConversationMessageResult\(|writeScopeMessageToConversationResult\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production MessagePlan applier calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'ApplyMessagePlanTx(WithSidecarWriter)?\(|applyMessagePlanTx\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production caller-owned delivery boundary calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'SendHumanMessage\(|SendAppMessageForUserWithOptions\(|SendMessageForUser(WithOptions|WithType|WithMetadata)?\(|SendSystemMessageForUser(WithMetadata)?\(|SendAgent(Thread)?Message(WithFreshness)?\(|Create(Channel|Thread)ShareCardForUser\(|ShareChannelShareCardForUser\(|CreateReviewFeedbackForUser\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production recoverable prepare/encode/emit calls\n'
  rg -n --glob '!**/*_test.go' -- \
    'preparePostCommitMessage\(|encodeRecoverablePostCommitDelivery\(|emitPostCommitMessage\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production committed-message fanout wiring\n'
  rg -n --glob '!**/*_test.go' -- \
    'SetCommittedMessageFanout\(|EmitCommittedMessageProjectionOnly\(|CreateAgentForUser\(|CreateWorkspaceAgentInTx\(' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'

  printf '\n## Current production recoverable-stream policy writes\n'
  rg -n --glob '!**/*_test.go' -- \
    'RecoverableStreamProjection[[:space:]]*[:=]' \
    internal cmd desktop 2>/dev/null || printf '(none)\n'
}

run_checks() {
  require_file guardrails/message/post-commit-producers.sh
  require_file guardrails/message/post-commit-producers-ast.go
  require_file guardrails/message/post-commit-producer-inventory.md
  require_file guardrails/message/direct-no-outbox-baseline.md
  require_file guardrails/message/README.md
  require_file scripts/ci-backend-required.sh
  require_file specs/message/scope-implementation-design.md

  require_file internal/store/app/app_service_messages.go
  require_file internal/store/app/message_append.go
  require_file internal/store/app/agent_messages.go
  require_file internal/store/privateagent/service_ports.go
  require_file internal/store/message/scope_applier.go
  require_file internal/store/app/committed_message_fanout.go
  require_file internal/store/app/service_event_delivery_dispatcher.go
  require_file internal/store/app/app_notification_service.go
  require_file internal/store/app/app_runtime_delivery.go
  require_file internal/store/app/knowledge_qa_messages.go
  require_file internal/store/app/platform_service_account_broadcast.go
  require_file internal/store/app/service_event_task_delivery.go
  require_file internal/store/app/channel_member_events.go
  require_file internal/store/app/agent_lifecycle.go
  require_file internal/store/taskboard/detail_mutation.go
  require_file internal/api/im/rewind_handler.go
  require_file internal/service/approvalapp/team_role_feedback.go
  require_file internal/server/wire.go
  require_file internal/api/testkit/runtime.go
  require_file cmd/agentteam-e2e-server/main.go

  require_contains guardrails/message/README.md './post-commit-producers.sh' 'message guardrail README must link the autonomous post-commit producer guardrail'
  require_contains guardrails/message/README.md './post-commit-producer-inventory.md' 'message guardrail README must link the autonomous post-commit producer inventory'
  require_contains guardrails/message/README.md 'autonomous post-commit producer' 'message guardrail README must describe the autonomous post-commit producer boundary'
  require_contains guardrails/message/README.md 'lowercase privateagent write seams' 'message guardrail README must describe lower privateagent seam enforcement'
  require_contains guardrails/message/README.md 'exact production function/count' 'message guardrail README must describe exact MessagePlan applier enforcement'
  require_contains guardrails/message/README.md 'applyMessagePlanTx' 'message guardrail README must name the final lowercase applier seam'
  require_contains guardrails/message/README.md 'scripts/ci-backend-required.sh validate' 'message guardrail README must name its Go-equipped CI owner'
  require_contains scripts/ci-backend-required.sh 'guardrails/message/post-commit-producers.sh --check-only' 'backend validation must execute the Go AST post-commit producer guardrail'

  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageProviderEvent' 'producer inventory must include Provider service events'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageAppNotification' 'producer inventory must include App Notifications'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageExternalAppRuntimeReply' 'producer inventory must include external App runtime replies'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageKnowledgeQA' 'producer inventory must include Knowledge Q&A replies'
  require_contains guardrails/message/post-commit-producer-inventory.md 'Platform Service Account broadcast' 'producer inventory must include platform broadcasts'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageCallerOwned' 'producer inventory must name caller-owned handoff'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appServiceMessageStoreOwnedRecoverable' 'producer inventory must name store-owned recoverable handoff'
  require_contains guardrails/message/post-commit-producer-inventory.md 'messageAppendCallerOwned' 'producer inventory must include caller-owned raw message appends'
  require_contains guardrails/message/post-commit-producer-inventory.md 'messageAppendAppServiceContracted' 'producer inventory must include the contracted raw App-service append'
  require_contains guardrails/message/post-commit-producer-inventory.md 'messageAppendStoreOwnedRecoverable' 'producer inventory must include the autonomous raw append policy'
  require_contains guardrails/message/post-commit-producer-inventory.md 'Outer caller-owned delivery boundaries' 'producer inventory must cover existing delivery-returning APIs above the raw append seam'
  require_contains guardrails/message/post-commit-producer-inventory.md 'writeConversationMessageResult' 'producer inventory must close the lowercase privateagent raw writer'
  require_contains guardrails/message/post-commit-producer-inventory.md 'writeScopeMessageToConversationResult' 'producer inventory must close the lowest privateagent raw writer'
  require_contains guardrails/message/post-commit-producer-inventory.md 'function-local call counts' 'producer inventory must describe exact MessagePlan applier call counts'
  require_contains guardrails/message/post-commit-producer-inventory.md 'message.applyMessagePlanTx' 'producer inventory must close the lowercase physical MessagePlan seam'
  require_contains guardrails/message/post-commit-producer-inventory.md 'postTeamRoleRejectionFeedback' 'producer inventory must keep the explicitly deferred Approval caller visible'
  require_contains guardrails/message/post-commit-producer-inventory.md 'status `pending`, not targeted-Task `pending_v2`' 'producer inventory must separate stable projection recovery from targeted unread status'

  require_contains guardrails/message/post-commit-producer-inventory.md 'internal/api/im/rewind_handler.go' 'producer inventory must list rewind as an intentional no-notify exception'
  require_contains guardrails/message/post-commit-producer-inventory.md 'internal/store/app/channel_member_events.go' 'producer inventory must list channel member events as an intentional no-notify exception'
  require_contains guardrails/message/post-commit-producer-inventory.md 'internal/service/channeladmin/service.go' 'producer inventory must list Agent Exec channel member effects as an intentional no-notify exception'
  require_contains guardrails/message/post-commit-producer-inventory.md 'CreateWorkspaceAgentInTx' 'producer inventory must document caller-owned agent-create member-event handoffs'
  require_contains guardrails/message/post-commit-producer-inventory.md 'regular creation, duplication, and role materialization' 'producer inventory must cover all production Agent-create variants'
  require_contains guardrails/message/post-commit-producer-inventory.md 'appendServiceEventTaskDiscussionTx' 'producer inventory must document the canonical service/app-authored Task discussion producer'
  require_contains guardrails/message/post-commit-producer-inventory.md 'anchorTaskDescriptionAttachments' 'producer inventory must list the hidden attachment anchor as an intentional no-notify exception'
  require_contains guardrails/message/post-commit-producer-inventory.md 'Approval Team rejection feedback' 'producer inventory must carry the explicit Approval deferral'
  require_contains guardrails/message/post-commit-producer-inventory.md 'NOT remediated by this guardrail' 'producer inventory must not claim Approval Team rejection feedback is fixed'

  require_contains internal/store/app/app_service_messages.go 'appServiceMessageDeliveryConfigurator' 'shared App-service append must own producer-specific projection before finalization'
  require_contains internal/store/app/app_service_messages.go 'finalizeAppServiceMessageTx' 'shared App-service append must finalize internally before returning'
  require_contains internal/store/app/message_append.go 'messageAppendLifecycle' 'lowest shared message append seam must require a typed lifecycle policy'
  require_contains internal/store/app/message_append.go 'appendMessageToThreadResult' 'freshness-aware raw message append must stay inside the lifecycle seam'
  require_contains internal/store/app/committed_message_fanout.go 'appServiceMessageContract' 'canonical seam must retain its closed producer contract'
  require_contains internal/store/app/committed_message_fanout.go 'appServiceMessagePostCommitOwner' 'canonical seam must retain its explicit post-commit owner type'
  require_contains internal/store/app/committed_message_fanout.go 'encodeRecoverablePostCommitDelivery' 'canonical seam must retain the recoverable encoding helper'
  require_contains internal/store/app/committed_message_fanout.go 'Outbox.SetPayloadForMessage' 'autonomous owner must persist the encoded payload in the creating transaction'
  require_contains internal/store/app/committed_message_fanout.go 'postCommitMessage' 'canonical seam must retain its opaque post-commit token'
  require_contains specs/message/scope-implementation-design.md 'exact function + count inventory' 'scope implementation spec must require structural MessagePlan applier inventory'
  require_contains specs/message/scope-implementation-design.md 'writeScopeMessageToConversationResult' 'scope implementation spec must close the lowest privateagent writer seam'
  require_contains specs/message/scope-implementation-design.md 'lowercase `applyMessagePlanTx`' 'scope implementation spec must close the final MessagePlan implementation seam'

  if ! command -v go >/dev/null 2>&1; then
    fail 'go is required to run the post-commit producer AST guardrail'
  elif ! go run "$ROOT/guardrails/message/post-commit-producers-ast.go" -root "$ROOT"; then
    fail 'autonomous post-commit producer AST inventory does not match production code'
  fi
}

case "${1:-}" in
  --inventory)
    print_inventory
    exit 0
    ;;
  --check-only|"")
    run_checks
    ;;
  *)
    printf 'Usage: %s [--check-only|--inventory]\n' "$0" >&2
    exit 2
    ;;
esac

if (( failures > 0 )); then
  printf '\nautonomous post-commit producer guardrail failed with %d issue(s).\n' "$failures" >&2
  exit 1
fi

printf 'autonomous post-commit producer guardrail passed.\n'
