package wire

import (
	"context"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeevents"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
)

// agentLifecycleRuntime controls AgentTeam-server-owned agent lifecycle events.
type agentLifecycleRuntime interface {
	StartAgentForAgentTeam(channelID, serverAgentID string, cfg types.AgentStartConfig, wake *types.Message) error
	StopByServerAgentID(serverAgentID string) bool
	InterruptByServerAgentID(serverAgentID string) bool
	InterruptActiveRunByServerAgentID(serverAgentID string) bool
	SleepByServerAgentID(serverAgentID string) bool
	UpdateAgentIdentity(serverAgentID, name, displayName string)
}

// deferredDeliveryRuntime manages AgentTeam deferred-delivery bookkeeping.
type deferredDeliveryRuntime interface {
	PrepareVisibleReply(agentID string, selectors ...string) string
	ConfirmReactionReply(agentID, wakeToken, messageID string) bool
	ReconcileDeferredDeliveries(serverAgentID string, messageIDs []string) ([]string, error)
}

// recoveryRuntime manages AgentTeam recovery artifacts owned by the dispatcher.
type recoveryRuntime interface {
	RestoreAgentRecoveryPoint(channelID, serverAgentID, requestID, runtime, model, sessionID, strategy, recoveryRef string) error
	GCRecoveryPoints(serverAgentID string, requests []types.RecoveryArtifactRequest) error
}

// trajectoryReaderRuntime reads trajectory history for AgentTeam MCP tools.
type trajectoryReaderRuntime interface {
	TrajectoryHistory(agentID string, limit int, before int64) []types.TrajectoryLogItem
}

// inboxMessageReconciler removes inbox entries already acked by an agent.
type inboxMessageReconciler interface {
	ReconcileInboxMessages(agentID string, messageIDs []string) error
}

// freshnessInboxMessageReader reads pending messages for one freshness target
// without consuming unrelated inbox deliveries.
type freshnessInboxMessageReader interface {
	InboxMessagesForFreshnessPage(agentID string, targetKey string, throughSeq int64, limit int) (types.InboxReadPage, error)
}

// freshnessRuntimeInboxMessageReader is the final-hop-aware variant used when
// the MCP transport can identify the exact runtime session/thread that will
// receive the result.
type freshnessRuntimeInboxMessageReader interface {
	InboxMessagesForFreshnessPageForRuntime(agentID string, targetKey string, throughSeq int64, limit int, runtimeSessionID string) (types.InboxReadPage, error)
}

// freshnessPendingRuntimeInputReader checks whether an ack-aware runtime input
// has already accepted target messages that can cover a held freshness gap once
// the runtime reaches the next input/model boundary.
type freshnessPendingRuntimeInputReader interface {
	PendingRuntimeInputCoversFreshness(agentID string, targetKey string, throughSeq int64) bool
}

// ackedDeliveryReconciler suppresses messages already acked by direct/busy delivery.
type ackedDeliveryReconciler interface {
	ReconcileRuntimeAckedDeliveries(agentID string, messageIDs []string) ([]string, error)
}

// runtimeMessageDeliveryPreparer applies the runtime ingress guard for messages
// acked through daemon-local tools before they enter runtime context.
type runtimeMessageDeliveryPreparer interface {
	PrepareRuntimeMessageToolDeliveries(agentID string, messages []types.InboxMessage) int
}

// runtimeDeliveryAcknowledger advances opaque tool-delivery receipts only
// after the owning transport reports its final response boundary.
type runtimeDeliveryAcknowledger interface {
	AcknowledgeRuntimeMessageToolDeliveriesForSession(agentID string, receipts []string, outcome types.MCPToolDeliveryOutcome, runtimeSessionID string)
}

// activeTaskTargetSetter records the latest task-native target for a session.
type activeTaskTargetSetter interface {
	SetActiveTaskTarget(agentID string, target types.ActiveTaskTarget) bool
}

// deliveryAckDispatcher dispatches a server-delivered message and reports
// whether the local handoff was actually accepted.
type deliveryAckDispatcher interface {
	DispatchForDeliveryAck(ctx context.Context, ch types.ServerConnection, msg *types.Message) error
}

// agentSessionSnapshotLister exposes a point-in-time view of all live sessions.
type agentSessionSnapshotLister interface {
	Sessions() []types.SessionSnapshot
}

// sessionStateChangeSubscriber streams dispatcher-local session state changes
// that should be reflected to AgentTeam server even when the runtime produces no
// new activity event.
type sessionStateChangeSubscriber interface {
	SubscribeSessionStateChanges(subscriberName string) runtimeevents.Subscription
}

// resettableDispatcher can reset a running session and its managed runtime.
type resettableDispatcher interface {
	ResetByAgent(agentID string) (bool, error)
}

// sessionResumeStateClearer clears daemon-local resume metadata without
// deleting the agent workspace.
type sessionResumeStateClearer interface {
	ClearSessionResumeStateByAgent(agentID string) error
}

// agentCatalogChangeNotifier lets the AgentTeam channel notify dashboard observers
// after a server catalog sync mutates local agent definitions.
type agentCatalogChangeNotifier interface {
	PublishAgentCatalogChanged(channelID string)
}

// agentExtensionCatalogReconciler owns the verified Desktop Catalog cache and
// process replacement lifecycle. Observation is synchronous/fail-closed;
// network fetch runs as a connection-owned task.
type agentExtensionCatalogReconciler interface {
	ObserveAgentExtensionCatalogHints(serverID string, agents []types.MachineAgentInfo)
	RefreshAgentExtensionCatalog(
		ctx context.Context,
		serverID string,
		agentID string,
		api *apiclient.Client,
	) error
}

type runningAgentConfigRefresher interface {
	RefreshRunningAgentConfig(cfg agent.Config) bool
}

// managedSkillLaunchReadinessChecker reports whether a fresh launch already
// has an authoritative managed-skill cache answer for the target agent.
type managedSkillLaunchReadinessChecker interface {
	ManagedSkillLaunchReady(agentID string) bool
}

type runningAgentStartConfigRefresher interface {
	RefreshRunningAgentStartConfig(serverAgentID string, cfg types.AgentStartConfig) bool
}

// memoryAccessRefresher re-evaluates memory watcher state for agents whose
// server-side memory access has changed.
type memoryAccessRefresher interface {
	RefreshMemoryAccess(ctx context.Context, agentIDs []string)
}

// skillAccessRefresher re-evaluates managed skill bindings for agents whose
// server-side skill access has changed. The dispatcher implementation
// (RefreshSkillAccess) calls SkillSyncer.SyncAgent per agent and updates
// any running session's managedSkillPaths. Idempotency at the event level is
// the caller's responsibility — the dispatcher just applies whatever set it
// is handed.
type skillAccessRefresher interface {
	RefreshSkillAccess(ctx context.Context, agentIDs []string)
}

// knowledgeBaseAccessRefresher re-evaluates managed KB repository bindings for
// agents whose server-side KB access has changed.
type knowledgeBaseAccessRefresher interface {
	RefreshKnowledgeBaseAccess(ctx context.Context, agentIDs []string)
}

// dispatcherRuntime is the consumer-local interface for the AgentTeam channel.
// It composes named capability groups instead of growing one monolithic method list.
//
// The concrete *dispatcher.Dispatcher satisfies this interface.
type dispatcherRuntime interface {
	types.ServerConnectionContext
	deliveryAckDispatcher
	agentLifecycleRuntime
	deferredDeliveryRuntime
	recoveryRuntime
	trajectoryReaderRuntime
}

// NOTE: The concrete *dispatcher.Dispatcher satisfies dispatcherRuntime.
// The compile-time assertion lives in the dispatcher package to avoid
// import cycles.
