package agent

import (
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

// RuntimeDeliveryPath identifies a boundary where a AgentTeam message becomes
// visible to an agent runtime. Every code path that places message content into
// runtime context must pass through PrepareRuntimeMessageDelivery so rewind
// invariants stay centralized instead of being re-implemented per delivery path.
type RuntimeDeliveryPath string

const (
	RuntimeDeliveryPathProtocol   RuntimeDeliveryPath = "protocol_delivery"
	RuntimeDeliveryPathStdin      RuntimeDeliveryPath = "stdin_delivery"
	RuntimeDeliveryPathToolOutput RuntimeDeliveryPath = "tool_output"
)

// PrepareRuntimeMessageDelivery runs the required pre/post conditions for a
// message that is being delivered into runtime context. Workflow selectors are
// message-local data: surfacing one message never owns or blocks later input.
func (ap *Process) PrepareRuntimeMessageDelivery(msg types.InboxMessage, path RuntimeDeliveryPath) bool {
	if !ap.prepareRuntimeMessageDeliveryRecovery(msg, path) {
		return false
	}
	ap.notifyRuntimeMessageDeliveryForTurn(msg, nil)
	return true
}

// prepareRuntimeMessageDeliveryRecovery establishes the rewind boundary before
// a transport write.
func (ap *Process) prepareRuntimeMessageDeliveryRecovery(msg types.InboxMessage, path RuntimeDeliveryPath) bool {
	if strings.TrimSpace(string(path)) == "" {
		return false
	}
	messageID := messageIDFromInboxMessage(msg)
	if messageID == "" {
		return false
	}
	if !ap.transientRuntimeRetryRedeliveryMatches(messageID) {
		ap.captureRecoveryPoint(messageID)
	}
	return true
}

func (ap *Process) notifyRuntimeMessageDeliveryForTurn(msg types.InboxMessage, expectedAckEpoch *uint64) {
	if messageIDFromInboxMessage(msg) == "" {
		return
	}
	ap.mu.Lock()
	defer ap.mu.Unlock()
	if ap.stopping || ap.state == processStateStopped {
		return
	}
	if expectedAckEpoch != nil && ap.ackEpoch != *expectedAckEpoch {
		return
	}
	ap.addSendTargetsLocked(msg)
	ap.addTurnDeliveryLocked(msg.Target)
	ap.workflowCompaction.ObserveRuntimeMessage(msg)
}

// PrepareRuntimeMessageDeliveries applies PrepareRuntimeMessageDelivery to a batch
// that will be delivered together, for example freshness context returned in a
// daemon-local tool result. The caller must pass only messages that will
// actually be returned to the runtime.
func (ap *Process) PrepareRuntimeMessageDeliveries(messages []types.InboxMessage, path RuntimeDeliveryPath) int {
	prepared := 0
	for _, msg := range messages {
		if ap.PrepareRuntimeMessageDelivery(msg, path) {
			prepared++
		}
	}
	return prepared
}

// PrepareRuntimeMessageDeliveryRecoveries establishes only the pre-delivery
// rewind boundaries for a batch whose final transport hop has not committed.
func (ap *Process) PrepareRuntimeMessageDeliveryRecoveries(messages []types.InboxMessage, path RuntimeDeliveryPath) int {
	prepared := 0
	for _, msg := range messages {
		if ap.prepareRuntimeMessageDeliveryRecovery(msg, path) {
			prepared++
		}
	}
	return prepared
}
