package app

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/core/routing"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

const appMentionEventAction = "mention"

type appMentionLookupKeys struct {
	Tokens    []string
	MemberIDs []string
}

type appMentionThreadTargetCandidate struct {
	ServiceDefinitionID   string
	ServiceInstallationID string
	ServiceMemberMemberID string
	ParentAuthority       bool
	DirectTargetAuthority bool
}

func mergeAppMentionThreadTargetCandidates(groups ...[]appMentionThreadTargetCandidate) []appMentionThreadTargetCandidate {
	byInstallation := make(map[string]appMentionThreadTargetCandidate)
	invalidInstallations := make(map[string]struct{})
	for _, group := range groups {
		for _, candidate := range group {
			candidate.ServiceDefinitionID = strings.TrimSpace(candidate.ServiceDefinitionID)
			candidate.ServiceInstallationID = strings.TrimSpace(candidate.ServiceInstallationID)
			candidate.ServiceMemberMemberID = strings.TrimSpace(candidate.ServiceMemberMemberID)
			if candidate.ServiceDefinitionID == "" || candidate.ServiceInstallationID == "" || candidate.ServiceMemberMemberID == "" {
				continue
			}
			if _, invalid := invalidInstallations[candidate.ServiceInstallationID]; invalid {
				continue
			}
			current, exists := byInstallation[candidate.ServiceInstallationID]
			if !exists {
				byInstallation[candidate.ServiceInstallationID] = candidate
				continue
			}
			// Both optimistic queries read the same installation/member identity
			// in one transaction. Treat a corrupt disagreement as ineligible
			// instead of constructing a lock plan from conflicting identities.
			if current.ServiceDefinitionID != candidate.ServiceDefinitionID ||
				current.ServiceMemberMemberID != candidate.ServiceMemberMemberID {
				delete(byInstallation, candidate.ServiceInstallationID)
				invalidInstallations[candidate.ServiceInstallationID] = struct{}{}
				continue
			}
			current.ParentAuthority = current.ParentAuthority || candidate.ParentAuthority
			current.DirectTargetAuthority = current.DirectTargetAuthority || candidate.DirectTargetAuthority
			byInstallation[candidate.ServiceInstallationID] = current
		}
	}
	candidates := make([]appMentionThreadTargetCandidate, 0, len(byInstallation))
	for _, candidate := range byInstallation {
		candidates = append(candidates, candidate)
	}
	sort.Slice(candidates, func(i, j int) bool {
		if candidates[i].ServiceDefinitionID != candidates[j].ServiceDefinitionID {
			return candidates[i].ServiceDefinitionID < candidates[j].ServiceDefinitionID
		}
		return candidates[i].ServiceInstallationID < candidates[j].ServiceInstallationID
	})
	return candidates
}

type appMentionSubscriptionCandidate struct {
	SubscriptionID           string
	ServiceDefinitionID      string
	ServiceInstallationID    string
	ServiceMemberID          string
	ServiceMemberMemberID    string
	ServiceMemberHandle      string
	ServiceMemberDisplayName string
	TargetKind               string
	TargetRef                string
	DeliveryScopeID          string
	PermissionCheckScopeID   string
	DefinitionSlug           string
	AppName                  string
}

type appMentionAuthorSnapshot struct {
	Type        string `json:"type"`
	MemberID    string `json:"member_id"`
	DisplayName string `json:"display_name"`
}

func (store *Store) handleAppMentionDeliveriesForMessage(
	ctx context.Context,
	tx coredb.Tx,
	authorMemberID string,
	authorType string,
	workspaceID string,
	target message.ThreadAccess,
	sourceMessageID string,
	messageType string,
	content string,
) ([]serviceactor.EventDelivery, error) {
	if !appMentionAuthorTypeCanTrigger(authorType) {
		return nil, nil
	}
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedAuthorMemberID := strings.TrimSpace(authorMemberID)
	trimmedSourceMessageID := strings.TrimSpace(sourceMessageID)
	if trimmedWorkspaceID == "" || trimmedAuthorMemberID == "" || trimmedSourceMessageID == "" {
		return nil, nil
	}

	routingContent := mentionRoutingContent(messageType, content)
	spans := routing.ParseDirectMentionSpansLimit(routingContent, message.MaxScopeDirectMentionTokens)
	if len(spans) == 0 {
		return nil, nil
	}

	resolvedTarget, err := store.resolveAppMentionTargetForMessage(ctx, tx, trimmedWorkspaceID, trimmedAuthorMemberID, target)
	if err != nil {
		return nil, err
	}
	isAllChannelDeliveryScope, err := appMentionDeliveryScopeIsAllChannel(ctx, tx, trimmedWorkspaceID, resolvedTarget.DeliveryScopeID)
	if err != nil {
		return nil, err
	}
	if isAllChannelDeliveryScope {
		// User-installed Apps are never participants in #all. Keep this
		// runtime fence for installations created before that invariant was
		// introduced: neither a legacy #all binding nor a thread binding under
		// #all may produce a delivery (or materialize any new thread state).
		return nil, nil
	}
	mentionKeys := appMentionLookupKeysForSpans(spans)
	now := time.Now().UTC()
	eligibleMemberIDs, err := store.ensureAppMentionThreadTargetBindingsFromParent(ctx, tx, trimmedWorkspaceID, trimmedAuthorMemberID, authorType, resolvedTarget, mentionKeys, now)
	if err != nil {
		return nil, err
	}
	if err := ensureAppMentionThreadTargetMemberships(ctx, tx, resolvedTarget, eligibleMemberIDs, now); err != nil {
		return nil, err
	}
	candidates, err := store.loadAppMentionSubscriptionCandidates(ctx, tx, trimmedWorkspaceID, resolvedTarget, mentionKeys)
	if err != nil {
		return nil, err
	}
	if len(candidates) == 0 {
		return nil, nil
	}

	author, err := loadAppMentionAuthorSnapshot(ctx, tx, trimmedWorkspaceID, trimmedAuthorMemberID, authorType)
	if err != nil {
		return nil, err
	}
	deliveries := make([]serviceactor.EventDelivery, 0, len(candidates))
	txServiceActor := store.serviceActorStore().InTx(tx)
	for _, candidate := range candidates {
		candidateSpans := appMentionSpansForCandidate(spans, candidate)
		if len(candidateSpans) == 0 {
			continue
		}
		eventKey := appMentionDeliveryEventKey(trimmedSourceMessageID, candidate.ServiceInstallationID)
		idempotencyKey := appMentionDeliveryIdempotencyKey(trimmedSourceMessageID, candidate.ServiceInstallationID)
		exists, err := appMentionDeliveryExistsByIdempotencyKey(ctx, tx, idempotencyKey)
		if err != nil {
			return nil, err
		}
		if exists {
			continue
		}
		payload, err := marshalAppMentionEventPayload(appMentionEventPayloadParams{
			WorkspaceID:     trimmedWorkspaceID,
			Candidate:       candidate,
			Target:          resolvedTarget,
			SourceMessageID: trimmedSourceMessageID,
			MessageType:     messageType,
			Content:         content,
			RoutingContent:  routingContent,
			Author:          author,
			MentionSpans:    candidateSpans,
			SourceTarget:    target,
			CreatedAt:       now,
		})
		if err != nil {
			return nil, err
		}
		delivery, duplicate, err := txServiceActor.ReserveEventDelivery(ctx, serviceactor.EventDeliveryReserve{
			WorkspaceID:          trimmedWorkspaceID,
			EventSubscriptionID:  candidate.SubscriptionID,
			EventType:            registry.AppEventMessageMention,
			EventAction:          appMentionEventAction,
			EventID:              trimmedSourceMessageID,
			EventKey:             eventKey,
			EventPayload:         payload,
			ExternalResourceType: AppMentionExternalResourceType,
			ExternalResourceKey:  eventKey,
			ExternalResourceRef:  trimmedSourceMessageID,
			IdempotencyKey:       idempotencyKey,
			Now:                  now,
		})
		if err != nil {
			return nil, err
		}
		if !duplicate {
			deliveries = append(deliveries, delivery)
		}
	}
	return deliveries, nil
}

func appMentionDeliveryScopeIsAllChannel(ctx context.Context, queryer coredb.QueryRower, workspaceID string, deliveryScopeID string) (bool, error) {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedDeliveryScopeID := strings.TrimSpace(deliveryScopeID)
	if trimmedWorkspaceID == "" || trimmedDeliveryScopeID == "" {
		return false, nil
	}
	var isAllChannel bool
	if err := queryer.QueryRow(ctx, `
SELECT EXISTS (
    SELECT 1
    FROM scopes delivery_scope
    JOIN scope_channel_profiles channel_profile
      ON channel_profile.workspace_id = delivery_scope.workspace_id
     AND channel_profile.scope_id = CASE
          WHEN delivery_scope.type = 'thread' THEN delivery_scope.parent_scope_id
          ELSE delivery_scope.id
     END
    WHERE delivery_scope.workspace_id = $1
      AND delivery_scope.id = $2
      AND delivery_scope.type IN ('channel', 'thread')
      AND (channel_profile.is_all_channel = TRUE OR channel_profile.slug = 'all')
)
`, trimmedWorkspaceID, trimmedDeliveryScopeID).Scan(&isAllChannel); err != nil {
		return false, fmt.Errorf("check App mention #all delivery surface: %w", err)
	}
	return isAllChannel, nil
}

func (store *Store) ensureAppMentionThreadTargetBindingsFromParent(
	ctx context.Context,
	tx coredb.Tx,
	workspaceID string,
	authorMemberID string,
	authorType string,
	target ServiceEventTarget,
	keys appMentionLookupKeys,
	now time.Time,
) (map[string]struct{}, error) {
	if keys.empty() || strings.TrimSpace(target.TargetKind) != "thread" {
		return nil, nil
	}
	directCandidates, err := store.loadAppMentionThreadCurrentTargetCandidates(
		ctx, tx, workspaceID, target, keys,
	)
	if err != nil {
		return nil, err
	}
	parentCandidates := make([]appMentionThreadTargetCandidate, 0)
	parentScopeID := strings.TrimSpace(target.PermissionCheckScopeID)
	if parentScopeID != "" && parentScopeID != strings.TrimSpace(target.DeliveryScopeID) {
		parentTargetKind, resolveErr := resolveAppMentionThreadParentTargetKind(
			ctx, tx, workspaceID, target, parentScopeID,
		)
		if resolveErr != nil {
			return nil, resolveErr
		}
		if parentTargetKind != "" {
			parentCandidates, err = store.loadAppMentionParentBoundCandidates(
				ctx, tx, workspaceID, parentTargetKind, parentScopeID, keys,
			)
			if err != nil {
				return nil, err
			}
		}
	}
	candidates := mergeAppMentionThreadTargetCandidates(parentCandidates, directCandidates)
	if len(candidates) == 0 {
		return nil, nil
	}
	bindActorType := strings.TrimSpace(authorType)
	if bindActorType == "user" {
		bindActorType = string(domain.MemberTypeHuman)
	}
	bindActorID, err := loadAppMentionBindingActorID(ctx, tx, workspaceID, authorMemberID, bindActorType)
	if err != nil {
		return nil, err
	}
	eligibleMemberIDs := make(map[string]struct{}, len(candidates))
	for _, candidate := range candidates {
		// Both authority categories were discovered before the first App lock.
		// The merged plan has one stable global definition/installation order,
		// so multi-App mentions cannot invert parent and direct lock phases.
		memberID, parentAuthorized, lockErr := lockAppMentionThreadTargetCandidate(
			ctx, store, tx, workspaceID, candidate, target,
		)
		if lockErr != nil {
			if errors.Is(lockErr, ErrNotFound) {
				// An optimistic authority candidate became stale while waiting for
				// the visibility fence. Skip only this App, not the user message.
				continue
			}
			return nil, fmt.Errorf("lock App mention thread target candidate: %w", lockErr)
		}
		if memberID != candidate.ServiceMemberMemberID {
			continue
		}
		eligibleMemberIDs[memberID] = struct{}{}
		if !parentAuthorized {
			continue
		}
		// Parent-bound App subscriptions only need to materialize the thread
		// target once. Human and agent follow-up mentions in the same thread must
		// reuse the existing binding/subscription instead of trying to create the
		// same active target identity again.
		alreadyBound, err := appMentionActiveTargetSubscriptionExists(
			ctx, tx, workspaceID, candidate.ServiceInstallationID, target,
		)
		if err != nil {
			return nil, err
		}
		if alreadyBound {
			continue
		}
		if _, err := store.bindAppInstallationTargetInTx(ctx, tx, bindAppTargetParams{
			WorkspaceID:        workspaceID,
			ActorMemberID:      authorMemberID,
			ActorType:          bindActorType,
			ActorID:            bindActorID,
			InstallationID:     candidate.ServiceInstallationID,
			Target:             target,
			CreatedVia:         AppMentionCreatedViaThread,
			EnsureMembership:   true,
			SubscriptionStatus: serviceactor.EventSubscriptionStatusActive,
			Now:                now,
		}); err != nil {
			return nil, fmt.Errorf("ensure App mention thread target binding from parent: %w", err)
		}
	}
	return eligibleMemberIDs, nil
}

func appMentionActiveTargetSubscriptionExists(ctx context.Context, queryer coredb.QueryRower, workspaceID string, installationID string, target ServiceEventTarget) (bool, error) {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedInstallationID := strings.TrimSpace(installationID)
	if trimmedWorkspaceID == "" || trimmedInstallationID == "" {
		return false, nil
	}
	var marker int
	if err := queryer.QueryRow(ctx, `
SELECT 1
FROM service_event_subscriptions subscription
JOIN service_installations installation
  ON installation.id = subscription.service_installation_id
 AND installation.workspace_id = subscription.workspace_id
 AND installation.service_kind = subscription.service_kind
 AND installation.status = 'active'
JOIN service_target_bindings binding
  ON binding.id = subscription.service_target_binding_id
 AND binding.workspace_id = subscription.workspace_id
 AND binding.service_definition_id = subscription.service_definition_id
 AND binding.service_installation_id = subscription.service_installation_id
 AND binding.service_member_id = subscription.service_member_id
 AND binding.service_kind = subscription.service_kind
 AND binding.target_kind = subscription.target_kind
 AND binding.target_ref = subscription.target_ref
 AND binding.delivery_scope_id = subscription.delivery_scope_id
 AND binding.permission_check_scope_id = subscription.permission_check_scope_id
 AND binding.status = 'active'
 AND binding.removed_at IS NULL
WHERE subscription.workspace_id = $1
  AND subscription.service_installation_id = $2
  AND subscription.subscription_kind = $3
  AND subscription.event_filter_key = $4
  AND subscription.target_kind = $5
  AND subscription.target_ref = $6
  AND subscription.delivery_scope_id = $7
  AND subscription.permission_check_scope_id = $8
  AND subscription.status = 'active'
  AND subscription.removed_at IS NULL
  AND JSON_CONTAINS(
      COALESCE(JSON_EXTRACT(
          installation.metadata,
          '$.manifest.capabilities.runtime.events'
      ), JSON_ARRAY()),
      JSON_QUOTE(subscription.event_filter_key)
  ) = 1
  AND EXISTS (
      SELECT 1
      FROM service_grants read_grant
      WHERE read_grant.service_installation_id = subscription.service_installation_id
        AND read_grant.scope = $9
        AND read_grant.revoked_at IS NULL
        AND (read_grant.expires_at IS NULL OR read_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND EXISTS (
      SELECT 1
      FROM service_grants write_grant
      WHERE write_grant.service_installation_id = subscription.service_installation_id
        AND write_grant.scope = $10
        AND write_grant.revoked_at IS NULL
        AND (write_grant.expires_at IS NULL OR write_grant.expires_at > CURRENT_TIMESTAMP)
  )
LIMIT 1
`, trimmedWorkspaceID, trimmedInstallationID, serviceactor.SubscriptionKindAppMention, registry.AppEventMessageMention, strings.TrimSpace(target.TargetKind), strings.TrimSpace(target.TargetRef), strings.TrimSpace(target.DeliveryScopeID), strings.TrimSpace(target.PermissionCheckScopeID), AppMentionScopeRead, AppRuntimeScopeMessageWrite).Scan(&marker); err != nil {
		if coredb.IsNotFound(err) {
			return false, nil
		}
		return false, fmt.Errorf("check active App mention target subscription: %w", err)
	}
	return true, nil
}

func loadAppMentionBindingActorID(ctx context.Context, queryer coredb.QueryRower, workspaceID string, authorMemberID string, actorType string) (string, error) {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedAuthorMemberID := strings.TrimSpace(authorMemberID)
	trimmedActorType := strings.TrimSpace(actorType)
	if trimmedActorType == "user" {
		trimmedActorType = string(domain.MemberTypeHuman)
	}
	if trimmedWorkspaceID == "" || trimmedAuthorMemberID == "" {
		return trimmedAuthorMemberID, nil
	}

	var actorID string
	switch trimmedActorType {
	case string(domain.MemberTypeHuman):
		if err := queryer.QueryRow(ctx, `
SELECT COALESCE(NULLIF(user_id, ''), id)
FROM members
WHERE workspace_id = $1
  AND id = $2
LIMIT 1
`, trimmedWorkspaceID, trimmedAuthorMemberID).Scan(&actorID); err != nil {
			if coredb.IsNotFound(err) {
				return trimmedAuthorMemberID, nil
			}
			return "", fmt.Errorf("load App mention human binding actor: %w", err)
		}
	case string(domain.MemberTypeAgent):
		if err := queryer.QueryRow(ctx, `
SELECT COALESCE(NULLIF(agent.id, ''), member.id)
FROM members member
LEFT JOIN agents agent
  ON agent.workspace_id = member.workspace_id
 AND agent.member_id = member.id
WHERE member.workspace_id = $1
  AND member.id = $2
LIMIT 1
`, trimmedWorkspaceID, trimmedAuthorMemberID).Scan(&actorID); err != nil {
			if coredb.IsNotFound(err) {
				return trimmedAuthorMemberID, nil
			}
			return "", fmt.Errorf("load App mention agent binding actor: %w", err)
		}
	default:
		return trimmedAuthorMemberID, nil
	}
	return firstNonEmpty(strings.TrimSpace(actorID), trimmedAuthorMemberID), nil
}

func resolveAppMentionThreadParentTargetKind(ctx context.Context, queryer coredb.QueryRower, workspaceID string, target ServiceEventTarget, parentScopeID string) (string, error) {
	trimmedParentScopeID := strings.TrimSpace(parentScopeID)
	if trimmedParentScopeID == "" {
		return "", nil
	}
	parentScope := target.ResolvedScopeTarget.Normalized().ParentScope
	parentTargetKind := strings.TrimSpace(string(parentScope.ScopeType))
	if strings.TrimSpace(parentScope.ScopeID) != trimmedParentScopeID {
		parentTargetKind = ""
	}
	if parentTargetKind == "" {
		if err := queryer.QueryRow(ctx, `
SELECT type
FROM scopes
WHERE workspace_id = $1
  AND id = $2
  AND status = 'active'
LIMIT 1
`, strings.TrimSpace(workspaceID), trimmedParentScopeID).Scan(&parentTargetKind); err != nil {
			if coredb.IsNotFound(err) {
				return "", nil
			}
			return "", fmt.Errorf("load App mention thread parent scope: %w", err)
		}
		parentTargetKind = strings.TrimSpace(parentTargetKind)
	}
	switch parentTargetKind {
	case "channel", "dm":
		return parentTargetKind, nil
	default:
		return "", nil
	}
}

func (store *Store) loadAppMentionParentBoundCandidates(
	ctx context.Context,
	tx coredb.Tx,
	workspaceID string,
	parentTargetKind string,
	parentScopeID string,
	keys appMentionLookupKeys,
) ([]appMentionThreadTargetCandidate, error) {
	if keys.empty() {
		return nil, nil
	}
	mentionedTokensCTE, mentionedTokenArgs, _, err := appMentionLookupKeysCTE(keys, 4)
	if err != nil {
		return nil, fmt.Errorf("build parent-bound App mention tokens: %w", err)
	}
	query := fmt.Sprintf(`
WITH %s
SELECT DISTINCT
    subscription.service_definition_id,
    subscription.service_installation_id,
    service_member.member_id
FROM service_event_subscriptions subscription
JOIN service_installations installation
  ON installation.id = subscription.service_installation_id
 AND installation.workspace_id = subscription.workspace_id
 AND installation.service_kind = subscription.service_kind
 AND installation.status = 'active'
JOIN service_definitions definition
  ON definition.id = subscription.service_definition_id
 AND definition.service_kind = subscription.service_kind
JOIN service_members service_member
  ON service_member.id = subscription.service_member_id
 AND service_member.service_installation_id = subscription.service_installation_id
 AND service_member.service_kind = subscription.service_kind
 AND service_member.status = 'active'
JOIN service_target_bindings binding
  ON binding.id = subscription.service_target_binding_id
 AND binding.workspace_id = subscription.workspace_id
 AND binding.service_definition_id = subscription.service_definition_id
 AND binding.service_installation_id = subscription.service_installation_id
 AND binding.service_member_id = subscription.service_member_id
 AND binding.service_kind = subscription.service_kind
 AND binding.target_kind = subscription.target_kind
 AND binding.target_ref = subscription.target_ref
 AND binding.delivery_scope_id = subscription.delivery_scope_id
 AND binding.permission_check_scope_id = subscription.permission_check_scope_id
 AND binding.status = 'active'
 AND binding.removed_at IS NULL
JOIN scope_memberships visible_membership
  ON visible_membership.scope_id = subscription.delivery_scope_id
 AND visible_membership.member_id = service_member.member_id
 AND visible_membership.visibility = 'public'
 AND visible_membership.removed_at IS NULL
 AND visible_membership.archived_at IS NULL
 AND visible_membership.effective_to_seq IS NULL
WHERE subscription.workspace_id = $1
  AND subscription.subscription_kind = $2
  AND subscription.event_filter_key = $3
  AND COALESCE(JSON_TYPE(JSON_EXTRACT(
      installation.metadata,
      '$.manifest.capabilities.runtime'
  )), '') = 'OBJECT'
  AND JSON_CONTAINS(
      COALESCE(JSON_EXTRACT(
          installation.metadata,
          '$.manifest.capabilities.runtime.events'
      ), JSON_ARRAY()),
      JSON_QUOTE(subscription.event_filter_key)
  ) = 1
  AND subscription.target_kind = $%d
  AND subscription.target_ref = $%d
  AND subscription.delivery_scope_id = $%d
  AND subscription.permission_check_scope_id = $%d
  AND subscription.status = 'active'
  AND subscription.removed_at IS NULL
  AND EXISTS (
      SELECT 1
      FROM service_grants read_grant
      WHERE read_grant.service_installation_id = subscription.service_installation_id
        AND read_grant.scope = $%d
        AND read_grant.revoked_at IS NULL
        AND (read_grant.expires_at IS NULL OR read_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND EXISTS (
      SELECT 1
      FROM service_grants write_grant
      WHERE write_grant.service_installation_id = subscription.service_installation_id
        AND write_grant.scope = $%d
        AND write_grant.revoked_at IS NULL
        AND (write_grant.expires_at IS NULL OR write_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND (
      EXISTS (
          SELECT 1
          FROM mentioned_tokens
          WHERE mentioned_tokens.token IN (
              LOWER(service_member.handle),
              LOWER(service_member.member_id),
              LOWER(service_member.display_name)
          )
      )
      OR EXISTS (
          SELECT 1
          FROM mentioned_member_ids
          WHERE mentioned_member_ids.member_id = service_member.member_id
      )
  )
ORDER BY
    subscription.service_definition_id ASC,
    subscription.service_installation_id ASC,
    service_member.member_id ASC
`, mentionedTokensCTE, 4+len(mentionedTokenArgs), 5+len(mentionedTokenArgs), 6+len(mentionedTokenArgs), 7+len(mentionedTokenArgs), 8+len(mentionedTokenArgs), 9+len(mentionedTokenArgs))
	args := []any{
		strings.TrimSpace(workspaceID),
		serviceactor.SubscriptionKindAppMention,
		registry.AppEventMessageMention,
	}
	args = append(args, mentionedTokenArgs...)
	args = append(args,
		strings.TrimSpace(parentTargetKind),
		strings.TrimSpace(parentScopeID),
		strings.TrimSpace(parentScopeID),
		strings.TrimSpace(parentScopeID),
		AppMentionScopeRead,
		AppRuntimeScopeMessageWrite,
	)
	rows, err := tx.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("load parent-bound App mention installations: %w", err)
	}
	defer rows.Close()
	candidates := make([]appMentionThreadTargetCandidate, 0)
	for rows.Next() {
		var candidate appMentionThreadTargetCandidate
		if err := rows.Scan(
			&candidate.ServiceDefinitionID,
			&candidate.ServiceInstallationID,
			&candidate.ServiceMemberMemberID,
		); err != nil {
			return nil, fmt.Errorf("scan parent-bound App mention installation: %w", err)
		}
		candidate.ServiceDefinitionID = strings.TrimSpace(candidate.ServiceDefinitionID)
		candidate.ServiceInstallationID = strings.TrimSpace(candidate.ServiceInstallationID)
		candidate.ServiceMemberMemberID = strings.TrimSpace(candidate.ServiceMemberMemberID)
		if candidate.ServiceDefinitionID == "" || candidate.ServiceInstallationID == "" || candidate.ServiceMemberMemberID == "" {
			continue
		}
		candidate.ParentAuthority = true
		candidates = append(candidates, candidate)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate parent-bound App mention installations: %w", err)
	}
	return candidates, nil
}

func appMentionAuthorTypeCanTrigger(authorType string) bool {
	switch strings.TrimSpace(authorType) {
	case string(domain.MemberTypeHuman), string(domain.MemberTypeAgent), "user":
		return true
	default:
		return false
	}
}

func (store *Store) resolveAppMentionTargetForMessage(ctx context.Context, tx coredb.Tx, workspaceID string, authorMemberID string, target message.ThreadAccess) (ServiceEventTarget, error) {
	scopeID := strings.TrimSpace(message.ScopeIDForThreadAccess(target))
	if scopeID == "" {
		return ServiceEventTarget{}, badRequest("message target is required")
	}
	var scopeType string
	if err := tx.QueryRow(ctx, `
SELECT type
FROM scopes
WHERE workspace_id = $1
  AND id = $2
  AND status = 'active'
LIMIT 1
`, strings.TrimSpace(workspaceID), scopeID).Scan(&scopeType); err != nil {
		if coredb.IsNotFound(err) {
			return ServiceEventTarget{}, ErrNotFound
		}
		return ServiceEventTarget{}, fmt.Errorf("load App mention target scope: %w", err)
	}
	if strings.TrimSpace(scopeType) == "task" {
		return resolveServiceEventTaskTargetByID(ctx, tx, workspaceID, authorMemberID, scopeID)
	}
	return ResolveServiceEventTargetForScope(ctx, tx, workspaceID, authorMemberID, scopeID)
}

func (store *Store) loadAppMentionThreadCurrentTargetCandidates(
	ctx context.Context,
	tx coredb.Tx,
	workspaceID string,
	target ServiceEventTarget,
	keys appMentionLookupKeys,
) ([]appMentionThreadTargetCandidate, error) {
	if keys.empty() || strings.TrimSpace(target.TargetKind) != "thread" {
		return nil, nil
	}
	mentionedTokensCTE, mentionedTokenArgs, _, err := appMentionLookupKeysCTE(keys, 4)
	if err != nil {
		return nil, fmt.Errorf("build App mention current-target tokens: %w", err)
	}
	query := fmt.Sprintf(`
WITH %s
SELECT DISTINCT
    subscription.service_definition_id,
    subscription.service_installation_id,
    service_member.member_id
FROM service_event_subscriptions subscription
JOIN service_installations installation
  ON installation.id = subscription.service_installation_id
 AND installation.workspace_id = subscription.workspace_id
 AND installation.service_kind = subscription.service_kind
 AND installation.status = 'active'
JOIN service_definitions definition
  ON definition.id = subscription.service_definition_id
 AND definition.service_kind = subscription.service_kind
JOIN service_members service_member
  ON service_member.id = subscription.service_member_id
 AND service_member.service_installation_id = subscription.service_installation_id
 AND service_member.service_kind = subscription.service_kind
 AND service_member.status = 'active'
JOIN service_target_bindings binding
  ON binding.id = subscription.service_target_binding_id
 AND binding.workspace_id = subscription.workspace_id
 AND binding.service_definition_id = subscription.service_definition_id
 AND binding.service_installation_id = subscription.service_installation_id
 AND binding.service_member_id = subscription.service_member_id
 AND binding.service_kind = subscription.service_kind
 AND binding.target_kind = subscription.target_kind
 AND binding.target_ref = subscription.target_ref
 AND binding.delivery_scope_id = subscription.delivery_scope_id
 AND binding.permission_check_scope_id = subscription.permission_check_scope_id
 AND binding.status = 'active'
 AND binding.removed_at IS NULL
WHERE subscription.workspace_id = $1
  AND subscription.subscription_kind = $2
  AND subscription.event_filter_key = $3
  AND COALESCE(JSON_TYPE(JSON_EXTRACT(
      installation.metadata,
      '$.manifest.capabilities.runtime'
  )), '') = 'OBJECT'
  AND JSON_CONTAINS(
      COALESCE(JSON_EXTRACT(
          installation.metadata,
          '$.manifest.capabilities.runtime.events'
      ), JSON_ARRAY()),
      JSON_QUOTE(subscription.event_filter_key)
  ) = 1
  AND subscription.target_kind = $%d
  AND subscription.target_ref = $%d
  AND subscription.delivery_scope_id = $%d
  AND subscription.status = 'active'
  AND subscription.removed_at IS NULL
  AND EXISTS (
      SELECT 1
      FROM service_grants read_grant
      WHERE read_grant.service_installation_id = subscription.service_installation_id
        AND read_grant.scope = $%d
        AND read_grant.revoked_at IS NULL
        AND (read_grant.expires_at IS NULL OR read_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND EXISTS (
      SELECT 1
      FROM service_grants write_grant
      WHERE write_grant.service_installation_id = subscription.service_installation_id
        AND write_grant.scope = $%d
        AND write_grant.revoked_at IS NULL
        AND (write_grant.expires_at IS NULL OR write_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND (
      EXISTS (
          SELECT 1
          FROM mentioned_tokens
          WHERE mentioned_tokens.token IN (
              LOWER(service_member.handle),
              LOWER(service_member.member_id),
              LOWER(service_member.display_name)
          )
      )
      OR EXISTS (
          SELECT 1
          FROM mentioned_member_ids
          WHERE mentioned_member_ids.member_id = service_member.member_id
      )
  )
ORDER BY
    subscription.service_definition_id ASC,
    subscription.service_installation_id ASC,
    service_member.member_id ASC
`, mentionedTokensCTE, 4+len(mentionedTokenArgs), 5+len(mentionedTokenArgs), 6+len(mentionedTokenArgs), 7+len(mentionedTokenArgs), 8+len(mentionedTokenArgs))
	args := []any{
		strings.TrimSpace(workspaceID),
		serviceactor.SubscriptionKindAppMention,
		registry.AppEventMessageMention,
	}
	args = append(args, mentionedTokenArgs...)
	args = append(args,
		strings.TrimSpace(target.TargetKind),
		strings.TrimSpace(target.TargetRef),
		strings.TrimSpace(target.DeliveryScopeID),
		AppMentionScopeRead,
		AppRuntimeScopeMessageWrite,
	)
	rows, err := tx.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("load App mention current-target candidates: %w", err)
	}
	defer rows.Close()
	candidates := make([]appMentionThreadTargetCandidate, 0)
	for rows.Next() {
		var candidate appMentionThreadTargetCandidate
		if err := rows.Scan(
			&candidate.ServiceDefinitionID,
			&candidate.ServiceInstallationID,
			&candidate.ServiceMemberMemberID,
		); err != nil {
			return nil, fmt.Errorf("scan App mention current-target candidate: %w", err)
		}
		candidate.ServiceDefinitionID = strings.TrimSpace(candidate.ServiceDefinitionID)
		candidate.ServiceInstallationID = strings.TrimSpace(candidate.ServiceInstallationID)
		candidate.ServiceMemberMemberID = strings.TrimSpace(candidate.ServiceMemberMemberID)
		if candidate.ServiceDefinitionID == "" || candidate.ServiceInstallationID == "" || candidate.ServiceMemberMemberID == "" {
			continue
		}
		candidate.DirectTargetAuthority = true
		candidates = append(candidates, candidate)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate App mention current-target candidates: %w", err)
	}
	return candidates, nil
}

func ensureAppMentionThreadTargetMemberships(ctx context.Context, tx coredb.Tx, target ServiceEventTarget, eligibleMemberIDs map[string]struct{}, now time.Time) error {
	if len(eligibleMemberIDs) == 0 || strings.TrimSpace(target.TargetKind) != "thread" {
		return nil
	}
	memberIDs := make([]string, 0, len(eligibleMemberIDs))
	for memberID := range eligibleMemberIDs {
		memberID = strings.TrimSpace(memberID)
		if memberID != "" {
			memberIDs = append(memberIDs, memberID)
		}
	}
	sort.Strings(memberIDs)
	for _, memberID := range memberIDs {
		if err := message.EnsureScopeMembership(ctx, tx, target.DeliveryScopeID, memberID, "public", "", "no-wake", 0, 0, now); err != nil {
			return fmt.Errorf("repair App mention thread target membership: %w", err)
		}
	}
	return nil
}

func (store *Store) loadAppMentionSubscriptionCandidates(ctx context.Context, tx coredb.Tx, workspaceID string, target ServiceEventTarget, keys appMentionLookupKeys) ([]appMentionSubscriptionCandidate, error) {
	if keys.empty() {
		return nil, nil
	}
	mentionedTokensCTE, mentionedTokenArgs, _, err := appMentionLookupKeysCTE(keys, 4)
	if err != nil {
		return nil, fmt.Errorf("build App mention tokens: %w", err)
	}
	query := fmt.Sprintf(`
WITH %s
SELECT DISTINCT
    subscription.id,
    subscription.service_definition_id,
    subscription.service_installation_id,
    subscription.service_member_id,
    service_member.member_id,
    service_member.handle,
    service_member.display_name,
    subscription.target_kind,
    subscription.target_ref,
    subscription.delivery_scope_id,
    subscription.permission_check_scope_id,
    definition.slug,
    definition.name
FROM service_event_subscriptions subscription
JOIN service_installations installation
  ON installation.id = subscription.service_installation_id
 AND installation.workspace_id = subscription.workspace_id
 AND installation.service_kind = subscription.service_kind
 AND installation.status = 'active'
JOIN service_definitions definition
  ON definition.id = subscription.service_definition_id
 AND definition.service_kind = subscription.service_kind
JOIN service_members service_member
  ON service_member.id = subscription.service_member_id
 AND service_member.service_installation_id = subscription.service_installation_id
 AND service_member.service_kind = subscription.service_kind
 AND service_member.status = 'active'
JOIN service_target_bindings binding
  ON binding.id = subscription.service_target_binding_id
 AND binding.workspace_id = subscription.workspace_id
 AND binding.service_definition_id = subscription.service_definition_id
 AND binding.service_installation_id = subscription.service_installation_id
 AND binding.service_member_id = subscription.service_member_id
 AND binding.service_kind = subscription.service_kind
 AND binding.target_kind = subscription.target_kind
 AND binding.target_ref = subscription.target_ref
 AND binding.delivery_scope_id = subscription.delivery_scope_id
 AND binding.permission_check_scope_id = subscription.permission_check_scope_id
 AND binding.status = 'active'
 AND binding.removed_at IS NULL
JOIN scope_memberships visible_membership
  ON visible_membership.scope_id = subscription.delivery_scope_id
 AND visible_membership.member_id = service_member.member_id
 AND visible_membership.visibility = 'public'
 AND visible_membership.removed_at IS NULL
 AND visible_membership.archived_at IS NULL
 AND visible_membership.effective_to_seq IS NULL
WHERE subscription.workspace_id = $1
  AND subscription.subscription_kind = $2
  AND subscription.event_filter_key = $3
  AND COALESCE(JSON_TYPE(JSON_EXTRACT(
      installation.metadata,
      '$.manifest.capabilities.runtime'
  )), '') = 'OBJECT'
  AND JSON_CONTAINS(
      COALESCE(JSON_EXTRACT(
          installation.metadata,
          '$.manifest.capabilities.runtime.events'
      ), JSON_ARRAY()),
      JSON_QUOTE(subscription.event_filter_key)
  ) = 1
  AND subscription.target_kind = $%d
  AND subscription.target_ref = $%d
  AND subscription.delivery_scope_id = $%d
  AND subscription.status = 'active'
  AND subscription.removed_at IS NULL
  AND EXISTS (
      SELECT 1
      FROM service_grants read_grant
      WHERE read_grant.service_installation_id = subscription.service_installation_id
        AND read_grant.scope = $%d
        AND read_grant.revoked_at IS NULL
        AND (read_grant.expires_at IS NULL OR read_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND EXISTS (
      SELECT 1
      FROM service_grants write_grant
      WHERE write_grant.service_installation_id = subscription.service_installation_id
        AND write_grant.scope = $%d
        AND write_grant.revoked_at IS NULL
        AND (write_grant.expires_at IS NULL OR write_grant.expires_at > CURRENT_TIMESTAMP)
  )
  AND (
      EXISTS (
          SELECT 1
          FROM mentioned_tokens
          WHERE mentioned_tokens.token IN (
              LOWER(service_member.handle),
              LOWER(service_member.member_id),
              LOWER(service_member.display_name)
          )
      )
      OR EXISTS (
          SELECT 1
          FROM mentioned_member_ids
          WHERE mentioned_member_ids.member_id = service_member.member_id
      )
  )
ORDER BY service_member.handle ASC, subscription.id ASC
`, mentionedTokensCTE, 4+len(mentionedTokenArgs), 5+len(mentionedTokenArgs), 6+len(mentionedTokenArgs), 7+len(mentionedTokenArgs), 8+len(mentionedTokenArgs))
	args := []any{
		strings.TrimSpace(workspaceID),
		serviceactor.SubscriptionKindAppMention,
		registry.AppEventMessageMention,
	}
	args = append(args, mentionedTokenArgs...)
	args = append(args,
		strings.TrimSpace(target.TargetKind),
		strings.TrimSpace(target.TargetRef),
		strings.TrimSpace(target.DeliveryScopeID),
		AppMentionScopeRead,
		AppRuntimeScopeMessageWrite,
	)
	rows, err := tx.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("load App mention subscription candidates: %w", err)
	}
	defer rows.Close()
	candidates := make([]appMentionSubscriptionCandidate, 0)
	for rows.Next() {
		var candidate appMentionSubscriptionCandidate
		if err := rows.Scan(
			&candidate.SubscriptionID,
			&candidate.ServiceDefinitionID,
			&candidate.ServiceInstallationID,
			&candidate.ServiceMemberID,
			&candidate.ServiceMemberMemberID,
			&candidate.ServiceMemberHandle,
			&candidate.ServiceMemberDisplayName,
			&candidate.TargetKind,
			&candidate.TargetRef,
			&candidate.DeliveryScopeID,
			&candidate.PermissionCheckScopeID,
			&candidate.DefinitionSlug,
			&candidate.AppName,
		); err != nil {
			return nil, fmt.Errorf("scan App mention subscription candidate: %w", err)
		}
		candidate.SubscriptionID = strings.TrimSpace(candidate.SubscriptionID)
		candidate.ServiceDefinitionID = strings.TrimSpace(candidate.ServiceDefinitionID)
		candidate.ServiceInstallationID = strings.TrimSpace(candidate.ServiceInstallationID)
		candidate.ServiceMemberID = strings.TrimSpace(candidate.ServiceMemberID)
		candidate.ServiceMemberMemberID = strings.TrimSpace(candidate.ServiceMemberMemberID)
		candidate.ServiceMemberHandle = strings.TrimSpace(candidate.ServiceMemberHandle)
		candidate.ServiceMemberDisplayName = strings.TrimSpace(candidate.ServiceMemberDisplayName)
		candidate.TargetKind = strings.TrimSpace(candidate.TargetKind)
		candidate.TargetRef = strings.TrimSpace(candidate.TargetRef)
		candidate.DeliveryScopeID = strings.TrimSpace(candidate.DeliveryScopeID)
		candidate.PermissionCheckScopeID = strings.TrimSpace(candidate.PermissionCheckScopeID)
		candidate.DefinitionSlug = strings.TrimSpace(candidate.DefinitionSlug)
		candidate.AppName = strings.TrimSpace(candidate.AppName)
		candidates = append(candidates, candidate)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate App mention subscription candidates: %w", err)
	}
	return candidates, nil
}

func appMentionDeliveryExistsByIdempotencyKey(ctx context.Context, queryer coredb.QueryRower, idempotencyKey string) (bool, error) {
	var marker int
	if err := queryer.QueryRow(ctx, `
SELECT 1
FROM service_event_deliveries
WHERE idempotency_key = $1
LIMIT 1
`, strings.TrimSpace(idempotencyKey)).Scan(&marker); err != nil {
		if coredb.IsNotFound(err) {
			return false, nil
		}
		return false, fmt.Errorf("check App mention delivery idempotency: %w", err)
	}
	return true, nil
}

func loadAppMentionAuthorSnapshot(ctx context.Context, queryer coredb.Queryer, workspaceID string, authorMemberID string, authorType string) (appMentionAuthorSnapshot, error) {
	snapshot := appMentionAuthorSnapshot{
		Type:        strings.TrimSpace(authorType),
		MemberID:    strings.TrimSpace(authorMemberID),
		DisplayName: strings.TrimSpace(authorMemberID),
	}
	if snapshot.Type == "user" {
		snapshot.Type = string(domain.MemberTypeHuman)
	}
	if snapshot.MemberID == "" {
		return snapshot, nil
	}
	var displayName string
	if err := queryer.QueryRow(ctx, `
SELECT COALESCE(
    NULLIF(member.display_name, ''),
    NULLIF(user_profile.display_name, ''),
    NULLIF(user_profile.name, ''),
    NULLIF(agent_profile.name, ''),
    NULLIF(service_member.display_name, ''),
    member.id
)
FROM members member
LEFT JOIN users user_profile
  ON member.type = 'human'
 AND user_profile.id = member.user_id
LEFT JOIN agents agent_profile
  ON member.type = 'agent'
 AND agent_profile.member_id = member.id
LEFT JOIN service_members service_member
  ON member.type = 'service'
 AND service_member.member_id = member.id
WHERE member.workspace_id = $1
  AND member.id = $2
LIMIT 1
`, strings.TrimSpace(workspaceID), snapshot.MemberID).Scan(&displayName); err != nil {
		if coredb.IsNotFound(err) {
			return snapshot, nil
		}
		return appMentionAuthorSnapshot{}, fmt.Errorf("load App mention author snapshot: %w", err)
	}
	snapshot.DisplayName = firstNonEmpty(strings.TrimSpace(displayName), snapshot.MemberID)
	return snapshot, nil
}

type appMentionEventPayloadParams struct {
	WorkspaceID     string
	Candidate       appMentionSubscriptionCandidate
	Target          ServiceEventTarget
	SourceMessageID string
	MessageType     string
	Content         string
	RoutingContent  string
	Author          appMentionAuthorSnapshot
	MentionSpans    []routing.DirectMentionSpan
	SourceTarget    message.ThreadAccess
	CreatedAt       time.Time
}

func marshalAppMentionEventPayload(params appMentionEventPayloadParams) ([]byte, error) {
	mentions := make([]map[string]any, 0, len(params.MentionSpans))
	for _, span := range params.MentionSpans {
		mentions = append(mentions, map[string]any{
			"type":              "service",
			"token":             span.Token,
			"service_member_id": params.Candidate.ServiceMemberID,
			"member_id":         params.Candidate.ServiceMemberMemberID,
			"handle":            params.Candidate.ServiceMemberHandle,
			"range": map[string]any{
				"start": span.Start,
				"end":   span.End,
			},
		})
	}
	payload := map[string]any{
		"event_type": registry.AppEventMessageMention,
		"workspace": map[string]any{
			"id": strings.TrimSpace(params.WorkspaceID),
		},
		"app": map[string]any{
			"app_slug":              params.Candidate.DefinitionSlug,
			"app_name":              params.Candidate.AppName,
			"service_definition_id": params.Candidate.ServiceDefinitionID,
			"installation_id":       params.Candidate.ServiceInstallationID,
			"service_member_id":     params.Candidate.ServiceMemberID,
			"member_id":             params.Candidate.ServiceMemberMemberID,
			"handle":                params.Candidate.ServiceMemberHandle,
			"display_name":          params.Candidate.ServiceMemberDisplayName,
		},
		"target": map[string]any{
			"kind":                      params.Target.TargetKind,
			"ref":                       params.Target.TargetRef,
			"delivery_scope_id":         params.Target.DeliveryScopeID,
			"permission_check_scope_id": params.Target.PermissionCheckScopeID,
		},
		"message": map[string]any{
			"id":           params.SourceMessageID,
			"type":         strings.TrimSpace(params.MessageType),
			"text":         params.Content,
			"routing_text": params.RoutingContent,
			"author":       params.Author,
			"mentions":     mentions,
		},
		"reply": map[string]any{
			"target_kind":         params.Target.TargetKind,
			"target_ref":          params.Target.TargetRef,
			"delivery_scope_id":   params.Target.DeliveryScopeID,
			"reply_to_message_id": params.SourceMessageID,
		},
		"created_at": params.CreatedAt.UTC().Format(time.RFC3339Nano),
	}
	return model.MarshalJSONObject("App mention event payload", payload)
}

func (keys appMentionLookupKeys) empty() bool {
	return len(keys.Tokens) == 0 && len(keys.MemberIDs) == 0
}

func appMentionLookupKeysForSpans(spans []routing.DirectMentionSpan) appMentionLookupKeys {
	tokenSeen := make(map[string]struct{}, len(spans))
	memberSeen := make(map[string]struct{}, len(spans))
	keys := appMentionLookupKeys{}
	for _, span := range spans {
		if span.Type == "service" {
			memberID := strings.TrimSpace(span.ID)
			if memberID != "" {
				if _, ok := memberSeen[memberID]; !ok {
					memberSeen[memberID] = struct{}{}
					keys.MemberIDs = append(keys.MemberIDs, memberID)
				}
			}
			continue
		}
		if span.Type != "" {
			continue
		}
		token := strings.ToLower(strings.TrimSpace(span.Token))
		if token != "" {
			if _, ok := tokenSeen[token]; !ok {
				tokenSeen[token] = struct{}{}
				keys.Tokens = append(keys.Tokens, token)
			}
		}
	}
	return keys
}

func appMentionLookupKeysCTE(keys appMentionLookupKeys, startArg int) (string, []any, int, error) {
	tokensCTE, tokenArgs, nextArg, err := appMentionStringValuesCTE(
		"mentioned_tokens",
		"token",
		keys.Tokens,
		startArg,
	)
	if err != nil {
		return "", nil, startArg, err
	}
	memberIDsCTE, memberArgs, nextArg, err := appMentionStringValuesCTE(
		"mentioned_member_ids",
		"member_id",
		keys.MemberIDs,
		nextArg,
	)
	if err != nil {
		return "", nil, startArg, err
	}
	args := append(tokenArgs, memberArgs...)
	return tokensCTE + ",\n" + memberIDsCTE, args, nextArg, nil
}

func appMentionStringValuesCTE(name string, column string, values []string, startArg int) (string, []any, int, error) {
	if len(values) == 0 {
		return fmt.Sprintf("%s AS (\n    SELECT NULL AS %s WHERE 1 = 0\n)", name, column), nil, startArg, nil
	}
	return coredb.BuildStringValuesCTE(name, column, values, startArg)
}

func appMentionSpansForCandidate(spans []routing.DirectMentionSpan, candidate appMentionSubscriptionCandidate) []routing.DirectMentionSpan {
	matches := make(map[string]struct{}, 3)
	for _, value := range []string{candidate.ServiceMemberHandle, candidate.ServiceMemberMemberID, candidate.ServiceMemberDisplayName} {
		if token := strings.ToLower(strings.TrimSpace(value)); token != "" {
			matches[token] = struct{}{}
		}
	}
	candidateMemberID := strings.TrimSpace(candidate.ServiceMemberMemberID)
	out := make([]routing.DirectMentionSpan, 0, len(spans))
	for _, span := range spans {
		if span.Type != "" {
			if span.Type == "service" && strings.TrimSpace(span.ID) == candidateMemberID {
				out = append(out, span)
			}
			continue
		}
		if _, ok := matches[strings.ToLower(strings.TrimSpace(span.Token))]; ok {
			out = append(out, span)
		}
	}
	return out
}

func appMentionDeliveryEventKey(sourceMessageID string, installationID string) string {
	return strings.TrimSpace(sourceMessageID) + ":" + strings.TrimSpace(installationID)
}

func appMentionDeliveryIdempotencyKey(sourceMessageID string, installationID string) string {
	return serviceactor.SubscriptionKindAppMention + ":" + appMentionDeliveryEventKey(sourceMessageID, installationID)
}
