package app

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/runeffect"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

const (
	appRuntimeReplyEffectIDPrefix  = "apprt:reply:"
	appRuntimeMaxMetadataBytes     = 64 * 1024
	appRuntimeMaxResultBytes       = 64 * 1024
	appRuntimeMaxResultValueCount  = 2000
	appRuntimeMaxResultValueDepth  = 30
	appRuntimeMaxFailureCodeBytes  = 128
	appRuntimeMaxFailureDetailSize = 4096
)

// AppRuntimeDeliveryAuth describes the current runtime authority for a App
// delivery action.
type AppRuntimeDeliveryAuth struct {
	BearerToken                string
	WorkspaceID                string
	ServiceDefinitionID        string
	ServiceInstallationID      string
	ServiceMemberID            string
	ServiceRuntimeCredentialID string
	ServiceRuntimeSessionID    string
	DeliveryID                 string
	AttemptID                  string
	Attempt                    serviceactor.DeliveryAttempt
	Delivery                   serviceactor.EventDelivery
}

// AppRuntimeReplyParams describes one App runtime reply effect.
type AppRuntimeReplyParams struct {
	AttemptID      string
	Content        string
	Metadata       map[string]any
	IdempotencyKey string
}

// AppRuntimeAckParams describes a App runtime delivery acknowledgement.
type AppRuntimeAckParams struct {
	AttemptID string
	Result    map[string]any
}

// AppRuntimeFailParams describes a App runtime delivery failure.
type AppRuntimeFailParams struct {
	AttemptID string
	Code      string
	Message   string
	Result    map[string]any
}

// AppRuntimeDeliveryActionResult returns the current delivery and attempt state.
type AppRuntimeDeliveryActionResult struct {
	Delivery serviceactor.EventDelivery
	Attempt  serviceactor.DeliveryAttempt
}

// AppRuntimeReplyResult returns one App reply effect and the current delivery state.
type AppRuntimeReplyResult struct {
	AppRuntimeDeliveryActionResult
	Effect    serviceactor.DeliveryEffect
	MessageID string
}

// AuthenticateAppRuntimeDelivery validates a runtime bearer token for a specific
// delivery action and returns the delivery workspace before callers mutate state.
func (store *Store) AuthenticateAppRuntimeDelivery(ctx context.Context, bearerToken string, deliveryID string, attemptID string) (AppRuntimeDeliveryAuth, error) {
	if strings.TrimSpace(bearerToken) == "" {
		return AppRuntimeDeliveryAuth{}, ErrUnauthorized
	}
	var auth AppRuntimeDeliveryAuth
	now := time.Now().UTC()
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		var err error
		auth, err = store.authorizeAppRuntimeDeliveryTx(ctx, tx, bearerToken, deliveryID, attemptID, now)
		return err
	}); err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	return auth, nil
}

// ReplyAppRuntimeDelivery writes one visible App reply and records the reply
// effect against the current delivery attempt.
func (store *Store) ReplyAppRuntimeDelivery(ctx context.Context, auth AppRuntimeDeliveryAuth, params AppRuntimeReplyParams) (AppRuntimeReplyResult, error) {
	content := strings.TrimSpace(params.Content)
	if content == "" {
		return AppRuntimeReplyResult{}, badRequest("App reply content is required")
	}
	if len(params.IdempotencyKey) > 128 {
		return AppRuntimeReplyResult{}, badRequest("App reply idempotency key is too long")
	}
	replyDigest, err := appRuntimeReplyPayloadDigest(content, params.Metadata)
	if err != nil {
		return AppRuntimeReplyResult{}, err
	}
	replyMetadata, err := appRuntimeReplyEffectMetadata(params.IdempotencyKey, replyDigest, params.Metadata)
	if err != nil {
		return AppRuntimeReplyResult{}, err
	}
	var result AppRuntimeReplyResult
	var committedMessage postCommitMessage
	now := time.Now().UTC()
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		current, err := store.authorizeAppRuntimeDeliveryTx(ctx, tx, auth.BearerToken, auth.DeliveryID, params.AttemptID, now)
		if err != nil {
			return fmt.Errorf("authorize App runtime reply: %w", err)
		}
		if err := authorizeAppRuntimeDeliveryReplay(current, auth); err != nil {
			return err
		}
		if strings.TrimSpace(current.Delivery.SubscriptionKind) != serviceactor.SubscriptionKindAppMention {
			return ErrForbidden
		}
		if err := requireAppRuntimeActiveCurrentAttempt(current, now); err != nil {
			return err
		}
		if _, err := requireActiveAppRuntimeTargetBinding(ctx, tx, current.Delivery); err != nil {
			return err
		}
		if effect, found, err := store.findAppRuntimeReplyEffectByIdempotency(ctx, tx, current.Delivery, current.Attempt.ID, params.IdempotencyKey, replyDigest); err != nil {
			return err
		} else if found {
			if strings.TrimSpace(effect.DeliveryAttemptID) != strings.TrimSpace(current.Attempt.ID) {
				attempt, err := store.markAppRuntimeAttemptDelivered(ctx, tx, current.Attempt, now)
				if err != nil {
					return err
				}
				current.Attempt = attempt
				effect, err = store.appendAppRuntimeReplyReplayEffect(ctx, tx, current, effect, params.IdempotencyKey, now)
				if err != nil {
					return err
				}
				current.Delivery, err = store.serviceActorStore().InTx(tx).LoadEventDeliveryForUpdate(ctx, current.Delivery.WorkspaceID, current.Delivery.ID)
				if err != nil {
					return err
				}
			}
			result = AppRuntimeReplyResult{
				AppRuntimeDeliveryActionResult: AppRuntimeDeliveryActionResult{Delivery: current.Delivery, Attempt: current.Attempt},
				Effect:                         effect,
				MessageID:                      effect.MessageID,
			}
			return nil
		}
		attempt, err := store.markAppRuntimeAttemptDelivered(ctx, tx, current.Attempt, now)
		if err != nil {
			return err
		}
		current.Attempt = attempt
		installation, err := store.loadAppRuntimeAppInstallationTx(ctx, tx, current.Delivery)
		if err != nil {
			return err
		}
		effect, messageID, committed, err := store.appendAppRuntimeReplyEffect(ctx, tx, installation, current, content, params, replyDigest, replyMetadata, now)
		if err != nil {
			return err
		}
		if strings.TrimSpace(effect.DeliveryAttemptID) != strings.TrimSpace(current.Attempt.ID) {
			effect, err = store.appendAppRuntimeReplyReplayEffect(ctx, tx, current, effect, params.IdempotencyKey, now)
			if err != nil {
				return err
			}
		}
		delivery, err := store.serviceActorStore().InTx(tx).LoadEventDeliveryForUpdate(ctx, current.Delivery.WorkspaceID, current.Delivery.ID)
		if err != nil {
			return err
		}
		result = AppRuntimeReplyResult{
			AppRuntimeDeliveryActionResult: AppRuntimeDeliveryActionResult{Delivery: delivery, Attempt: attempt},
			Effect:                         effect,
			MessageID:                      messageID,
		}
		committedMessage = committed
		return nil
	}); err != nil {
		return AppRuntimeReplyResult{}, err
	}
	store.emitPostCommitMessage(ctx, committedMessage)
	return result, nil
}

// AckAppRuntimeDelivery marks the delivery as succeeded using the latest
// current-attempt effect allowed by that delivery's App runtime kind.
func (store *Store) AckAppRuntimeDelivery(ctx context.Context, auth AppRuntimeDeliveryAuth, params AppRuntimeAckParams) (AppRuntimeDeliveryActionResult, error) {
	resultJSON, err := appRuntimeResultJSON(params.Result)
	if err != nil {
		return AppRuntimeDeliveryActionResult{}, err
	}
	var result AppRuntimeDeliveryActionResult
	now := time.Now().UTC()
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		current, err := store.authorizeAppRuntimeDeliveryTx(ctx, tx, auth.BearerToken, auth.DeliveryID, params.AttemptID, now)
		if err != nil {
			return fmt.Errorf("authorize App runtime delivery acknowledgement: %w", err)
		}
		if err := authorizeAppRuntimeDeliveryReplay(current, auth); err != nil {
			return err
		}
		switch strings.TrimSpace(current.Delivery.Status) {
		case string(runeffect.StatusSucceeded):
			if strings.TrimSpace(current.Attempt.Status) != serviceactor.DeliveryAttemptStatusAcked {
				return conflict("App delivery is already succeeded by another attempt")
			}
			if !appRuntimeResultJSONEqual(current.Attempt.ResultJSON, resultJSON) {
				return conflict("App delivery acknowledgement replay does not match the terminal result")
			}
			result = AppRuntimeDeliveryActionResult{Delivery: current.Delivery, Attempt: current.Attempt}
			return nil
		case string(runeffect.StatusFailed):
			return conflict("App delivery is already failed")
		}
		if err := requireAppRuntimeActiveCurrentAttempt(current, now); err != nil {
			return err
		}
		effect, err := store.latestAppRuntimeCompletionEffect(ctx, tx, current)
		if err != nil {
			return fmt.Errorf("load App runtime completion effect: %w", err)
		}
		attempt, err := store.serviceActorStore().InTx(tx).SetDeliveryAttemptStatus(ctx, serviceactor.DeliveryAttemptStatusUpdate{
			WorkspaceID: current.Delivery.WorkspaceID,
			ID:          current.Attempt.ID,
			Status:      serviceactor.DeliveryAttemptStatusAcked,
			ResultJSON:  resultJSON,
			Now:         now,
		})
		if err != nil {
			return fmt.Errorf("acknowledge App runtime delivery attempt: %w", err)
		}
		if err := store.serviceActorStore().InTx(tx).CompleteEventDelivery(ctx, serviceactor.EventDeliveryCompletion{
			ID:     current.Delivery.ID,
			Status: runeffect.StatusSucceeded,
			Effect: runeffect.Effect{Kind: runeffect.EffectKind(effect.EffectKind), Ref: effect.EffectRef, MessageID: effect.MessageID},
			Now:    now,
		}); err != nil {
			return fmt.Errorf("complete App runtime delivery: %w", err)
		}
		if current.Delivery.SubscriptionKind == serviceactor.SubscriptionKindAppNotificationAction {
			if err := store.completeAppNotificationActionForDeliveryInTx(ctx, tx, current.Delivery, AppNotificationActionStateAcked, "", params.Result, now); err != nil {
				return err
			}
			if err := store.retireAppNotificationActionSubscriptionAfterDeliveryInTx(ctx, tx, current.Delivery, now); err != nil {
				return err
			}
		}
		delivery, err := store.serviceActorStore().InTx(tx).LoadEventDeliveryForUpdate(ctx, current.Delivery.WorkspaceID, current.Delivery.ID)
		if err != nil {
			return fmt.Errorf("reload acknowledged App runtime delivery: %w", err)
		}
		result = AppRuntimeDeliveryActionResult{Delivery: delivery, Attempt: attempt}
		return nil
	}); err != nil {
		return AppRuntimeDeliveryActionResult{}, err
	}
	return result, nil
}

// FailAppRuntimeDelivery records a runtime failure and completes the delivery as
// failed with standard run-effect diagnostics.
func (store *Store) FailAppRuntimeDelivery(ctx context.Context, auth AppRuntimeDeliveryAuth, params AppRuntimeFailParams) (AppRuntimeDeliveryActionResult, error) {
	code := strings.TrimSpace(params.Code)
	if code == "" {
		code = "app_runtime_failed"
	}
	detail := strings.TrimSpace(params.Message)
	if len(code) > appRuntimeMaxFailureCodeBytes || len(detail) > appRuntimeMaxFailureDetailSize {
		return AppRuntimeDeliveryActionResult{}, badRequest("App runtime failure code or message is too long")
	}
	resultJSON, err := appRuntimeResultJSON(params.Result)
	if err != nil {
		return AppRuntimeDeliveryActionResult{}, err
	}
	var result AppRuntimeDeliveryActionResult
	now := time.Now().UTC()
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		current, err := store.authorizeAppRuntimeDeliveryTx(ctx, tx, auth.BearerToken, auth.DeliveryID, params.AttemptID, now)
		if err != nil {
			return err
		}
		if err := authorizeAppRuntimeDeliveryReplay(current, auth); err != nil {
			return err
		}
		switch strings.TrimSpace(current.Delivery.Status) {
		case string(runeffect.StatusFailed):
			if strings.TrimSpace(current.Attempt.Status) != serviceactor.DeliveryAttemptStatusFailed {
				return conflict("App delivery is already failed by another attempt")
			}
			if strings.TrimSpace(current.Attempt.ErrorCode) != code ||
				strings.TrimSpace(current.Attempt.ErrorDetail) != detail ||
				!appRuntimeResultJSONEqual(current.Attempt.ResultJSON, resultJSON) {
				return conflict("App delivery failure replay does not match the terminal outcome")
			}
			result = AppRuntimeDeliveryActionResult{Delivery: current.Delivery, Attempt: current.Attempt}
			return nil
		case string(runeffect.StatusSucceeded):
			return conflict("App delivery is already succeeded")
		}
		if err := requireAppRuntimeActiveCurrentAttempt(current, now); err != nil {
			return err
		}
		attempt, err := store.serviceActorStore().InTx(tx).SetDeliveryAttemptStatus(ctx, serviceactor.DeliveryAttemptStatusUpdate{
			WorkspaceID: current.Delivery.WorkspaceID,
			ID:          current.Attempt.ID,
			Status:      serviceactor.DeliveryAttemptStatusFailed,
			ErrorCode:   code,
			ErrorDetail: detail,
			ResultJSON:  resultJSON,
			Now:         now,
		})
		if err != nil {
			return err
		}
		if err := store.serviceActorStore().InTx(tx).CompleteEventDelivery(ctx, serviceactor.EventDeliveryCompletion{
			ID:     current.Delivery.ID,
			Status: runeffect.StatusFailed,
			Effect: runeffect.NoEffect(),
			Error:  runeffect.Error{Code: code, Detail: detail},
			Now:    now,
		}); err != nil {
			return err
		}
		if current.Delivery.SubscriptionKind == serviceactor.SubscriptionKindAppNotificationAction {
			if err := store.completeAppNotificationActionForDeliveryInTx(ctx, tx, current.Delivery, AppNotificationActionStateFailed, code, params.Result, now); err != nil {
				return err
			}
			if err := store.retireAppNotificationActionSubscriptionAfterDeliveryInTx(ctx, tx, current.Delivery, now); err != nil {
				return err
			}
		}
		delivery, err := store.serviceActorStore().InTx(tx).LoadEventDeliveryForUpdate(ctx, current.Delivery.WorkspaceID, current.Delivery.ID)
		if err != nil {
			return err
		}
		result = AppRuntimeDeliveryActionResult{Delivery: delivery, Attempt: attempt}
		return nil
	}); err != nil {
		return AppRuntimeDeliveryActionResult{}, err
	}
	return result, nil
}

func (store *Store) authorizeAppRuntimeDeliveryTx(ctx context.Context, tx coredb.Tx, bearerToken string, deliveryID string, attemptID string, now time.Time) (AppRuntimeDeliveryAuth, error) {
	deliveryID = strings.TrimSpace(deliveryID)
	attemptID = strings.TrimSpace(attemptID)
	if deliveryID == "" || attemptID == "" {
		return AppRuntimeDeliveryAuth{}, badRequest("App delivery and attempt are required")
	}
	if strings.TrimSpace(bearerToken) != "" {
		if auth, err := store.authorizeAppRuntimeDeliveryWithRunnerTokenTx(ctx, tx, bearerToken, deliveryID, attemptID, now); err == nil {
			auth.BearerToken = strings.TrimSpace(bearerToken)
			return auth, nil
		} else if !errors.Is(err, ErrUnauthorized) && !errors.Is(err, serviceactor.ErrNotFound) && !errors.Is(err, storeerr.ErrNotFound) {
			return AppRuntimeDeliveryAuth{}, err
		}
		auth, err := store.authorizeAppRuntimeDeliveryWithAttemptTokenTx(ctx, tx, bearerToken, deliveryID, attemptID, now)
		if err != nil {
			return AppRuntimeDeliveryAuth{}, err
		}
		auth.BearerToken = strings.TrimSpace(bearerToken)
		return auth, nil
	}
	return AppRuntimeDeliveryAuth{}, ErrUnauthorized
}

func (store *Store) authorizeAppRuntimeDeliveryWithRunnerTokenTx(ctx context.Context, tx coredb.Tx, bearerToken string, deliveryID string, attemptID string, now time.Time) (AppRuntimeDeliveryAuth, error) {
	txServiceActor := store.serviceActorStore().InTx(tx)
	credential, err := txServiceActor.AuthenticateRuntimeCredential(ctx, bearerToken)
	if err != nil {
		if errors.Is(err, serviceactor.ErrNotFound) || strings.Contains(err.Error(), "runtime credential") {
			return AppRuntimeDeliveryAuth{}, ErrUnauthorized
		}
		return AppRuntimeDeliveryAuth{}, err
	}
	currentCredential, err := store.ValidateAppRunnerCredential(ctx, credential)
	if err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	attempt, err := txServiceActor.LoadDeliveryAttemptForUpdate(ctx, credential.WorkspaceID, attemptID)
	if err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	if strings.TrimSpace(attempt.TransportKind) != serviceactor.RuntimeTransportKindRunnerStream ||
		strings.TrimSpace(attempt.ServiceRuntimeCredentialID) != strings.TrimSpace(credential.ID) {
		return AppRuntimeDeliveryAuth{}, ErrUnauthorized
	}
	delivery, err := txServiceActor.LoadEventDeliveryForUpdate(ctx, credential.WorkspaceID, deliveryID)
	if err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	if err := store.requireCurrentAppRuntimeDeliveryGrant(ctx, delivery); err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	return store.buildAppRuntimeDeliveryAuth(appRuntimeInstallationAuthorityFromCredential(currentCredential), delivery, attempt, now)
}

func (store *Store) authorizeAppRuntimeDeliveryWithAttemptTokenTx(ctx context.Context, tx coredb.Tx, bearerToken string, deliveryID string, attemptID string, now time.Time) (AppRuntimeDeliveryAuth, error) {
	txServiceActor := store.serviceActorStore().InTx(tx)
	attempt, err := txServiceActor.AuthenticateDeliveryAttemptToken(ctx, bearerToken, deliveryID, attemptID, now)
	if err != nil {
		if errors.Is(err, serviceactor.ErrNotFound) || strings.Contains(err.Error(), "attempt token") || strings.Contains(err.Error(), "delivery attempt token") {
			return AppRuntimeDeliveryAuth{}, ErrUnauthorized
		}
		return AppRuntimeDeliveryAuth{}, err
	}
	delivery, err := txServiceActor.LoadEventDeliveryForUpdate(ctx, attempt.WorkspaceID, deliveryID)
	if err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	current, err := store.requireCurrentAppRuntimeInstallation(ctx, delivery.WorkspaceID, delivery.ServiceInstallationID, delivery.SubscriptionKind)
	if err != nil {
		return AppRuntimeDeliveryAuth{}, err
	}
	return store.buildAppRuntimeDeliveryAuth(current, delivery, attempt, now)
}

type appRuntimeInstallationAuthority struct {
	WorkspaceID           string
	ServiceDefinitionID   string
	ServiceInstallationID string
	ServiceMemberID       string
}

func (store *Store) requireCurrentAppRuntimeInstallation(ctx context.Context, workspaceID string, installationID string, subscriptionKind string) (appRuntimeInstallationAuthority, error) {
	requiredScopes, err := appRuntimeDeliveryRequiredScopes(subscriptionKind)
	if err != nil {
		return appRuntimeInstallationAuthority{}, err
	}
	summary, err := store.requireActiveAppRuntimeInstallation(ctx, workspaceID, installationID, requiredScopes)
	if err != nil {
		return appRuntimeInstallationAuthority{}, err
	}
	return appRuntimeInstallationAuthority{
		WorkspaceID:           summary.Installation.WorkspaceID,
		ServiceDefinitionID:   summary.Installation.ServiceDefinitionID,
		ServiceInstallationID: summary.Installation.ID,
		ServiceMemberID:       summary.Member.ID,
	}, nil
}

func (store *Store) requireCurrentAppRuntimeDeliveryGrant(ctx context.Context, delivery serviceactor.EventDelivery) error {
	_, err := store.requireCurrentAppRuntimeInstallation(ctx, delivery.WorkspaceID, delivery.ServiceInstallationID, delivery.SubscriptionKind)
	return err
}

func appRuntimeDeliveryRequiredScopes(subscriptionKind string) ([]string, error) {
	switch strings.TrimSpace(subscriptionKind) {
	case serviceactor.SubscriptionKindAppMention:
		return []string{AppMentionScopeRead, AppRuntimeScopeMessageWrite}, nil
	case serviceactor.SubscriptionKindAppNotificationAction:
		return []string{AppNotificationActionScopeRead}, nil
	default:
		return nil, ErrForbidden
	}
}

func appRuntimeInstallationAuthorityFromCredential(credential serviceactor.RuntimeCredential) appRuntimeInstallationAuthority {
	return appRuntimeInstallationAuthority{
		WorkspaceID:           credential.WorkspaceID,
		ServiceDefinitionID:   credential.ServiceDefinitionID,
		ServiceInstallationID: credential.ServiceInstallationID,
		ServiceMemberID:       credential.ServiceMemberID,
	}
}

func (store *Store) buildAppRuntimeDeliveryAuth(current appRuntimeInstallationAuthority, delivery serviceactor.EventDelivery, attempt serviceactor.DeliveryAttempt, now time.Time) (AppRuntimeDeliveryAuth, error) {
	if strings.TrimSpace(delivery.ID) == "" || strings.TrimSpace(attempt.ID) == "" {
		return AppRuntimeDeliveryAuth{}, ErrUnauthorized
	}
	if strings.TrimSpace(delivery.WorkspaceID) != strings.TrimSpace(attempt.WorkspaceID) || strings.TrimSpace(delivery.ID) != strings.TrimSpace(attempt.ServiceEventDeliveryID) {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	if strings.TrimSpace(delivery.ServiceInstallationID) != strings.TrimSpace(attempt.ServiceInstallationID) || strings.TrimSpace(delivery.ServiceInstallationID) != strings.TrimSpace(current.ServiceInstallationID) {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	if strings.TrimSpace(delivery.ServiceMemberID) != strings.TrimSpace(attempt.ServiceMemberID) || strings.TrimSpace(delivery.ServiceMemberID) != strings.TrimSpace(current.ServiceMemberID) {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	if !serviceactor.IsAppRuntimeSubscriptionKind(delivery.SubscriptionKind) {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	if strings.TrimSpace(delivery.ServiceKind) != serviceactor.KindApp {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	if strings.TrimSpace(delivery.DeliveryPolicy) != serviceactor.AppRuntimeDeliveryPolicyExternal ||
		strings.TrimSpace(delivery.ServiceConnectionRef) != serviceactor.AppRuntimeConnectionRefExternal {
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	switch strings.TrimSpace(attempt.TransportKind) {
	case serviceactor.RuntimeTransportKindCallback:
		if strings.TrimSpace(attempt.ServiceRuntimeCredentialID) != "" || strings.TrimSpace(attempt.ServiceRuntimeSessionID) != "" {
			return AppRuntimeDeliveryAuth{}, ErrForbidden
		}
	case serviceactor.RuntimeTransportKindRunnerStream:
		if strings.TrimSpace(attempt.ServiceRuntimeCredentialID) == "" || strings.TrimSpace(attempt.ServiceRuntimeSessionID) == "" {
			return AppRuntimeDeliveryAuth{}, ErrForbidden
		}
	default:
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	auth := AppRuntimeDeliveryAuth{
		WorkspaceID:                delivery.WorkspaceID,
		ServiceDefinitionID:        delivery.ServiceDefinitionID,
		ServiceInstallationID:      delivery.ServiceInstallationID,
		ServiceMemberID:            delivery.ServiceMemberID,
		ServiceRuntimeCredentialID: strings.TrimSpace(attempt.ServiceRuntimeCredentialID),
		ServiceRuntimeSessionID:    strings.TrimSpace(attempt.ServiceRuntimeSessionID),
		DeliveryID:                 delivery.ID,
		AttemptID:                  attempt.ID,
		Attempt:                    attempt,
		Delivery:                   delivery,
	}
	switch strings.TrimSpace(attempt.Status) {
	case serviceactor.DeliveryAttemptStatusLeased, serviceactor.DeliveryAttemptStatusDelivered:
		if err := requireAppRuntimeActiveCurrentAttempt(auth, now); err != nil {
			return AppRuntimeDeliveryAuth{}, err
		}
	case serviceactor.DeliveryAttemptStatusAcked:
		if strings.TrimSpace(delivery.Status) != string(runeffect.StatusSucceeded) {
			return AppRuntimeDeliveryAuth{}, ErrForbidden
		}
	case serviceactor.DeliveryAttemptStatusFailed:
		if strings.TrimSpace(delivery.Status) != string(runeffect.StatusFailed) {
			return AppRuntimeDeliveryAuth{}, ErrForbidden
		}
	default:
		return AppRuntimeDeliveryAuth{}, ErrForbidden
	}
	return auth, nil
}

func authorizeAppRuntimeDeliveryReplay(current AppRuntimeDeliveryAuth, expected AppRuntimeDeliveryAuth) error {
	if strings.TrimSpace(current.WorkspaceID) != strings.TrimSpace(expected.WorkspaceID) ||
		strings.TrimSpace(current.ServiceInstallationID) != strings.TrimSpace(expected.ServiceInstallationID) ||
		strings.TrimSpace(current.DeliveryID) != strings.TrimSpace(expected.DeliveryID) ||
		strings.TrimSpace(current.AttemptID) != strings.TrimSpace(expected.AttemptID) {
		return ErrForbidden
	}
	if strings.TrimSpace(expected.ServiceRuntimeCredentialID) != "" && strings.TrimSpace(current.ServiceRuntimeCredentialID) != strings.TrimSpace(expected.ServiceRuntimeCredentialID) {
		return ErrForbidden
	}
	return nil
}

func appRuntimeDeliveryStatusAcceptsRuntimeAction(status string) bool {
	switch strings.TrimSpace(status) {
	case string(runeffect.StatusRunning), string(runeffect.StatusRetryWaiting):
		return true
	default:
		return false
	}
}

func requireAppRuntimeActiveCurrentAttempt(auth AppRuntimeDeliveryAuth, now time.Time) error {
	if !appRuntimeDeliveryStatusAcceptsRuntimeAction(auth.Delivery.Status) {
		return conflict("App delivery is not active")
	}
	switch strings.TrimSpace(auth.Attempt.Status) {
	case serviceactor.DeliveryAttemptStatusLeased, serviceactor.DeliveryAttemptStatusDelivered:
	default:
		return conflict("App delivery attempt is not active")
	}
	if !appRuntimeDeliveryLeaseMatchesAttempt(auth.Delivery, auth.Attempt) {
		return conflict("App delivery attempt is no longer current")
	}
	if auth.Attempt.LeaseExpiresAt != nil && !auth.Attempt.LeaseExpiresAt.After(now.UTC()) {
		return ErrUnauthorized
	}
	return nil
}

func appRuntimeDeliveryLeaseMatchesAttempt(delivery serviceactor.EventDelivery, attempt serviceactor.DeliveryAttempt) bool {
	if strings.TrimSpace(delivery.ID) == "" || strings.TrimSpace(delivery.ID) != strings.TrimSpace(attempt.ServiceEventDeliveryID) {
		return false
	}
	if strings.TrimSpace(delivery.LeaseOwner) != strings.TrimSpace(attempt.LeaseOwner) {
		return false
	}
	if delivery.LeaseExpiresAt == nil || attempt.LeaseExpiresAt == nil {
		return delivery.LeaseExpiresAt == nil && attempt.LeaseExpiresAt == nil
	}
	return delivery.LeaseExpiresAt.Equal(*attempt.LeaseExpiresAt)
}

func (store *Store) markAppRuntimeAttemptDelivered(ctx context.Context, tx coredb.Tx, attempt serviceactor.DeliveryAttempt, now time.Time) (serviceactor.DeliveryAttempt, error) {
	if strings.TrimSpace(attempt.Status) == serviceactor.DeliveryAttemptStatusDelivered {
		return attempt, nil
	}
	return store.serviceActorStore().InTx(tx).MarkDeliveryAttemptDelivered(ctx, serviceactor.DeliveryAttemptDeliveredMark{
		WorkspaceID: strings.TrimSpace(attempt.WorkspaceID),
		AttemptID:   strings.TrimSpace(attempt.ID),
		Now:         now,
	})
}

func (store *Store) loadAppRuntimeAppInstallationTx(ctx context.Context, tx coredb.Tx, delivery serviceactor.EventDelivery) (model.RuntimeAppInstallation, error) {
	row := tx.QueryRow(ctx, runtimeAppInstallationSelectSQL()+`
WHERE installation.id = $1
  AND installation.workspace_id = $2
  AND installation.service_kind = $3
	AND installation.status = 'active'
	AND service_member.status = 'active'
  AND service_member.id = $4
`, strings.TrimSpace(delivery.ServiceInstallationID), strings.TrimSpace(delivery.WorkspaceID), serviceactor.KindApp, strings.TrimSpace(delivery.ServiceMemberID))
	installation, err := scanOptionalRuntimeAppInstallation(row)
	if err != nil {
		return model.RuntimeAppInstallation{}, err
	}
	return installation, nil
}

func (store *Store) appendAppRuntimeReplyEffect(ctx context.Context, tx coredb.Tx, installation model.RuntimeAppInstallation, auth AppRuntimeDeliveryAuth, content string, params AppRuntimeReplyParams, payloadDigest string, effectMetadata []byte, now time.Time) (serviceactor.DeliveryEffect, string, postCommitMessage, error) {
	binding := appRuntimeDeliveryTargetBinding(auth.Delivery)
	var effect runeffect.Effect
	var messageID string
	var committedMessage postCommitMessage
	if strings.TrimSpace(auth.Delivery.TargetKind) == "task" {
		var err error
		var replayed *serviceactor.DeliveryEffect
		effect, messageID, committedMessage, replayed, err = appendAppRuntimeTaskReplyEffect(
			ctx, store, tx, installation, auth, binding, content, params, payloadDigest, now,
		)
		if err != nil {
			return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
		}
		if replayed != nil {
			return *replayed, messageID, postCommitMessage{}, nil
		}
	} else {
		target, err := loadServiceEventDeliveryTarget(ctx, tx, binding)
		if err != nil {
			return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
		}
		options := appendMessageOptions{
			Source:          appRuntimeDeliveryContributionSource(auth.Delivery),
			ClientMessageID: appRuntimeReplyClientMessageID(auth.Delivery.ID, params.IdempotencyKey),
		}
		delivery, preparedMessage, err := store.appendAppServiceMessageWithOptions(
			ctx, tx, installation, target, content, params.Metadata, true, options,
			appServiceMessageExternalAppRuntimeReply,
			nil,
		)
		if err != nil {
			return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
		}
		messageID = delivery.MessageID
		if delivery.IdempotentReplay {
			existing, found, err := store.findAppRuntimeReplyEffectByIdempotency(ctx, tx, auth.Delivery, auth.Attempt.ID, params.IdempotencyKey, payloadDigest)
			if err != nil {
				return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
			}
			if !found || strings.TrimSpace(existing.MessageID) != strings.TrimSpace(messageID) {
				return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, conflict("App reply message replay is missing its canonical effect receipt")
			}
			return existing, messageID, postCommitMessage{}, nil
		}
		committedMessage = preparedMessage
		effect = runeffect.MessageEffect(delivery.MessageID)
	}
	normalizedEffect, err := effect.Normalize()
	if err != nil {
		return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
	}
	appended, err := store.serviceActorStore().InTx(tx).AppendDeliveryEffect(ctx, serviceactor.DeliveryEffectAppend{
		ID:                appRuntimeReplyEffectID(auth.Delivery.ID, auth.Attempt.ID, params.IdempotencyKey),
		WorkspaceID:       auth.Delivery.WorkspaceID,
		DeliveryAttemptID: auth.Attempt.ID,
		EffectKind:        string(normalizedEffect.Kind),
		EffectRef:         normalizedEffect.Ref,
		TargetKind:        auth.Delivery.TargetKind,
		TargetRef:         auth.Delivery.TargetRef,
		MessageID:         normalizedEffect.MessageID,
		Status:            serviceactor.DeliveryEffectStatusCreated,
		Metadata:          effectMetadata,
		Now:               now,
	})
	if err != nil {
		return serviceactor.DeliveryEffect{}, "", postCommitMessage{}, err
	}
	if messageID == "" {
		messageID = normalizedEffect.MessageID
	}
	return appended, messageID, committedMessage, nil
}

func (store *Store) appendAppRuntimeReplyReplayEffect(ctx context.Context, tx coredb.Tx, auth AppRuntimeDeliveryAuth, canonical serviceactor.DeliveryEffect, idempotencyKey string, now time.Time) (serviceactor.DeliveryEffect, error) {
	if !appRuntimeEffectAllowedForSubscription(serviceactor.SubscriptionKindAppMention, canonical) {
		return serviceactor.DeliveryEffect{}, conflict("App reply idempotency receipt has an invalid effect")
	}
	return store.serviceActorStore().InTx(tx).AppendDeliveryEffect(ctx, serviceactor.DeliveryEffectAppend{
		ID:                appRuntimeReplyEffectID(auth.Delivery.ID, auth.Attempt.ID, idempotencyKey),
		WorkspaceID:       auth.Delivery.WorkspaceID,
		DeliveryAttemptID: auth.Attempt.ID,
		EffectKind:        canonical.EffectKind,
		EffectRef:         canonical.EffectRef,
		TargetKind:        auth.Delivery.TargetKind,
		TargetRef:         auth.Delivery.TargetRef,
		MessageID:         canonical.MessageID,
		Status:            serviceactor.DeliveryEffectStatusCreated,
		Metadata:          canonical.Metadata,
		Now:               now,
	})
}

func appRuntimeDeliveryTargetBinding(delivery serviceactor.EventDelivery) serviceEventDeliveryTargetBinding {
	return serviceEventDeliveryTargetBinding{
		ID:                     strings.TrimSpace(delivery.ServiceTargetBindingID),
		WorkspaceID:            strings.TrimSpace(delivery.WorkspaceID),
		TargetKind:             strings.TrimSpace(delivery.TargetKind),
		TargetRef:              strings.TrimSpace(delivery.TargetRef),
		DeliveryScopeID:        strings.TrimSpace(delivery.DeliveryScopeID),
		PermissionCheckScopeID: strings.TrimSpace(delivery.PermissionCheckScopeID),
		ServiceDefinitionID:    strings.TrimSpace(delivery.ServiceDefinitionID),
		ServiceInstallationID:  strings.TrimSpace(delivery.ServiceInstallationID),
		ServiceMemberID:        strings.TrimSpace(delivery.ServiceMemberID),
		Status:                 serviceactor.StatusActive,
	}
}

func appRuntimeDeliveryContributionSource(delivery serviceactor.EventDelivery) message.ContributionSource {
	return message.ContributionSourceForServiceEventDelivery(message.ServiceEventDeliveryContributionSourceParams{
		DeliveryID:  delivery.ID,
		Label:       "App mention",
		EventID:     delivery.EventID,
		EventType:   delivery.EventType,
		EventAction: delivery.EventAction,
	})
}

func (store *Store) findAppRuntimeReplyEffectByIdempotency(ctx context.Context, tx coredb.Tx, delivery serviceactor.EventDelivery, attemptID string, key string, payloadDigest string) (serviceactor.DeliveryEffect, bool, error) {
	trimmedKey := strings.TrimSpace(key)
	if trimmedKey == "" {
		return serviceactor.DeliveryEffect{}, false, nil
	}
	prefix := appRuntimeReplyEffectIDKeyPrefix(delivery.ID, trimmedKey)
	effect, found, err := loadAppRuntimeReplyEffectRow(tx.QueryRow(ctx, `
SELECT id, workspace_id, service_event_delivery_id, service_event_delivery_attempt_id,
       service_installation_id, service_member_id, effect_kind, effect_ref,
       target_kind, target_ref, COALESCE(message_id, ''), status, metadata,
       created_at, updated_at
FROM service_event_delivery_effects
WHERE workspace_id = $1
  AND service_event_delivery_id = $2
  AND id LIKE $3
ORDER BY (service_event_delivery_attempt_id = $4) DESC, created_at ASC, id ASC
LIMIT 1
`, strings.TrimSpace(delivery.WorkspaceID), strings.TrimSpace(delivery.ID), prefix+"%", strings.TrimSpace(attemptID)))
	if err != nil {
		return serviceactor.DeliveryEffect{}, false, fmt.Errorf("find App runtime reply effect by idempotency key: %w", err)
	}
	if !found {
		// Compatibility fallback for reply effects written before deterministic
		// per-key effect ids were introduced. New writes use the indexed primary
		// key prefix above and never depend on a bounded diagnostics list.
		effect, found, err = loadAppRuntimeReplyEffectRow(tx.QueryRow(ctx, `
SELECT id, workspace_id, service_event_delivery_id, service_event_delivery_attempt_id,
       service_installation_id, service_member_id, effect_kind, effect_ref,
       target_kind, target_ref, COALESCE(message_id, ''), status, metadata,
       created_at, updated_at
FROM service_event_delivery_effects
WHERE workspace_id = $1
  AND service_event_delivery_id = $2
  AND JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.kind')) = 'app_runtime_reply'
  AND JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.idempotencyKey')) = $3
ORDER BY (service_event_delivery_attempt_id = $4) DESC, created_at ASC, id ASC
LIMIT 1
`, strings.TrimSpace(delivery.WorkspaceID), strings.TrimSpace(delivery.ID), trimmedKey, strings.TrimSpace(attemptID)))
		if err != nil {
			return serviceactor.DeliveryEffect{}, false, fmt.Errorf("find legacy App runtime reply effect by idempotency key: %w", err)
		}
	}
	if !found {
		return serviceactor.DeliveryEffect{}, false, nil
	}
	var decoded map[string]any
	if err := model.UnmarshalJSONObject("App runtime reply effect metadata", effect.Metadata, &decoded); err != nil {
		return serviceactor.DeliveryEffect{}, false, err
	}
	if strings.TrimSpace(fmt.Sprint(decoded["idempotencyKey"])) != trimmedKey {
		return serviceactor.DeliveryEffect{}, false, conflict("App reply idempotency receipt does not match its effect id")
	}
	if !strings.EqualFold(strings.TrimSpace(fmt.Sprint(decoded["payloadDigest"])), strings.TrimSpace(payloadDigest)) {
		return serviceactor.DeliveryEffect{}, false, conflict("App reply idempotency key was already used with a different payload")
	}
	return effect, true, nil
}

func loadAppRuntimeReplyEffectRow(row coredb.Row) (serviceactor.DeliveryEffect, bool, error) {
	var effect serviceactor.DeliveryEffect
	var metadata []byte
	if err := row.Scan(
		&effect.ID,
		&effect.WorkspaceID,
		&effect.ServiceEventDeliveryID,
		&effect.DeliveryAttemptID,
		&effect.ServiceInstallationID,
		&effect.ServiceMemberID,
		&effect.EffectKind,
		&effect.EffectRef,
		&effect.TargetKind,
		&effect.TargetRef,
		&effect.MessageID,
		&effect.Status,
		&metadata,
		&effect.CreatedAt,
		&effect.UpdatedAt,
	); err != nil {
		if coredb.IsNotFound(err) {
			return serviceactor.DeliveryEffect{}, false, nil
		}
		return serviceactor.DeliveryEffect{}, false, err
	}
	effect.Metadata = metadata
	return effect, true, nil
}

func (store *Store) latestAppRuntimeCompletionEffect(ctx context.Context, tx coredb.Tx, auth AppRuntimeDeliveryAuth) (serviceactor.DeliveryEffect, error) {
	rows, err := tx.Query(ctx, `
SELECT id, workspace_id, service_event_delivery_id, service_event_delivery_attempt_id,
       service_installation_id, service_member_id, effect_kind, effect_ref,
       target_kind, target_ref, COALESCE(message_id, ''), status, metadata,
       created_at, updated_at
FROM service_event_delivery_effects
WHERE workspace_id = $1
  AND service_event_delivery_id = $2
  AND service_event_delivery_attempt_id = $3
  AND status = $4
ORDER BY created_at DESC, id DESC
`, strings.TrimSpace(auth.Delivery.WorkspaceID), strings.TrimSpace(auth.Delivery.ID), strings.TrimSpace(auth.Attempt.ID), serviceactor.DeliveryEffectStatusCreated)
	if err != nil {
		return serviceactor.DeliveryEffect{}, fmt.Errorf("list App runtime completion effects for current attempt: %w", err)
	}
	defer rows.Close()
	for rows.Next() {
		effect, _, err := loadAppRuntimeReplyEffectRow(rows)
		if err != nil {
			return serviceactor.DeliveryEffect{}, fmt.Errorf("scan App runtime completion effect: %w", err)
		}
		if appRuntimeEffectAllowedForSubscription(auth.Delivery.SubscriptionKind, effect) {
			return effect, nil
		}
	}
	if err := rows.Err(); err != nil {
		return serviceactor.DeliveryEffect{}, fmt.Errorf("iterate App runtime completion effects: %w", err)
	}
	switch strings.TrimSpace(auth.Delivery.SubscriptionKind) {
	case serviceactor.SubscriptionKindAppMention:
		return serviceactor.DeliveryEffect{}, badRequest("App mention delivery requires a reply effect before ack")
	case serviceactor.SubscriptionKindAppNotificationAction:
		return serviceactor.DeliveryEffect{}, badRequest("App notification action delivery requires a notification mutation effect before ack")
	default:
		return serviceactor.DeliveryEffect{}, ErrForbidden
	}
}

func appRuntimeEffectAllowedForSubscription(subscriptionKind string, effect serviceactor.DeliveryEffect) bool {
	switch strings.TrimSpace(subscriptionKind) {
	case serviceactor.SubscriptionKindAppMention:
		switch runeffect.EffectKind(strings.TrimSpace(effect.EffectKind)) {
		case runeffect.EffectKindMessage, runeffect.EffectKindTaskDiscussion:
			candidate := runeffect.Effect{Kind: runeffect.EffectKind(effect.EffectKind), Ref: effect.EffectRef, MessageID: effect.MessageID}
			return candidate.Validate() == nil
		}
	case serviceactor.SubscriptionKindAppNotificationAction:
		switch runeffect.EffectKind(strings.TrimSpace(effect.EffectKind)) {
		case runeffect.EffectKindMessageUpdate, runeffect.EffectKindNotificationResolved, runeffect.EffectKindMessageWithdrawal:
			candidate := runeffect.Effect{Kind: runeffect.EffectKind(effect.EffectKind), Ref: effect.EffectRef, MessageID: effect.MessageID}
			return candidate.Validate() == nil
		}
	}
	return false
}

func appRuntimeReplyEffectMetadata(idempotencyKey string, payloadDigest string, metadata map[string]any) ([]byte, error) {
	payload := map[string]any{
		"kind":          "app_runtime_reply",
		"payloadDigest": strings.TrimSpace(payloadDigest),
	}
	if trimmed := strings.TrimSpace(idempotencyKey); trimmed != "" {
		payload["idempotencyKey"] = trimmed
	}
	if metadata != nil {
		payload["replyMetadata"] = metadata
	}
	encoded, err := model.MarshalJSONObject("App runtime reply effect metadata", payload)
	if err != nil {
		return nil, badRequest("App reply metadata must be JSON")
	}
	if len(encoded) > appRuntimeMaxMetadataBytes {
		return nil, badRequest("App reply metadata is too large")
	}
	return encoded, nil
}

func appRuntimeReplyPayloadDigest(content string, metadata map[string]any) (string, error) {
	digest, err := appNotificationDigest(struct {
		Content  string         `json:"content"`
		Metadata map[string]any `json:"metadata"`
	}{
		Content:  strings.TrimSpace(content),
		Metadata: metadata,
	})
	if err != nil {
		return "", badRequest("App reply metadata must be JSON")
	}
	return digest, nil
}

func appRuntimeResultJSON(result map[string]any) ([]byte, error) {
	if len(result) == 0 {
		return nil, nil
	}
	valueCount := 0
	if err := validateAppRuntimeJSONValue(result, 0, &valueCount); err != nil {
		return nil, err
	}
	encoded, err := model.MarshalJSONObject("App runtime result", result)
	if err != nil {
		return nil, badRequest("App runtime result must be JSON")
	}
	if len(encoded) > appRuntimeMaxResultBytes {
		return nil, badRequest("App runtime result is too large")
	}
	return encoded, nil
}

func validateAppRuntimeJSONValue(value any, depth int, count *int) error {
	if depth > appRuntimeMaxResultValueDepth {
		return badRequest("App runtime result is too deeply nested")
	}
	(*count)++
	if *count > appRuntimeMaxResultValueCount {
		return badRequest("App runtime result has too many values")
	}
	switch typed := value.(type) {
	case map[string]any:
		for key, child := range typed {
			if strings.TrimSpace(key) == "" || len(key) > 255 {
				return badRequest("App runtime result contains an invalid key")
			}
			if err := validateAppRuntimeJSONValue(child, depth+1, count); err != nil {
				return err
			}
		}
	case []any:
		for _, child := range typed {
			if err := validateAppRuntimeJSONValue(child, depth+1, count); err != nil {
				return err
			}
		}
	}
	return nil
}

func appRuntimeResultJSONEqual(left []byte, right []byte) bool {
	return model.JSONDocumentsEqual(left, right)
}

func appRuntimeReplyClientMessageID(deliveryID string, idempotencyKey string) string {
	trimmedKey := strings.TrimSpace(idempotencyKey)
	if trimmedKey == "" {
		return ""
	}
	digest := sha256.Sum256([]byte(strings.TrimSpace(deliveryID) + ":" + trimmedKey))
	return appRuntimeReplyEffectIDPrefix + hex.EncodeToString(digest[:])[:48]
}

func appRuntimeReplyEffectIDKeyPrefix(deliveryID string, idempotencyKey string) string {
	trimmedKey := strings.TrimSpace(idempotencyKey)
	if trimmedKey == "" {
		return ""
	}
	digest := sha256.Sum256([]byte("app-runtime-reply-effect\x00" + strings.TrimSpace(deliveryID) + "\x00" + trimmedKey))
	return "apprt:reply-effect:v1:" + hex.EncodeToString(digest[:]) + ":"
}

func appRuntimeReplyEffectID(deliveryID string, attemptID string, idempotencyKey string) string {
	prefix := appRuntimeReplyEffectIDKeyPrefix(deliveryID, idempotencyKey)
	if prefix == "" {
		return ""
	}
	attemptDigest := sha256.Sum256([]byte(strings.TrimSpace(attemptID)))
	return prefix + hex.EncodeToString(attemptDigest[:])
}
