package mcp

import (
	"context"
	"encoding/json"
	"errors"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/messagesend"
	"github.com/tidbcloud/AgentTeam/desktop/internal/metrics"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
)

// AgentTeamConnection is the consumer-owned view of one remote AgentTeam Server
// connection. wire.Protocol implements it without importing api/mcp.
type AgentTeamConnection interface {
	ID() string
	APIClient(agentID ...string) *apiclient.Client
	Send(context.Context, types.Metadata, types.OutboundMessage) error
}

// MessageHandlerConfig contains the required remote connection and runtime
// dependencies for one message handler.
type MessageHandlerConfig struct {
	Connection AgentTeamConnection
	Runtime    MessageRuntime
	// SendScopeProvider is the required narrow, message-send-only scope
	// port. message_send guards its targets against the caller-bound turn
	// scope; other tools can never consume it.
	SendScopeProvider SendScopeProvider
}

// SendScopeProvider is the narrow, message-send-only port for the
// target guard. It is deliberately separate from the shared MessageRuntime
// surface so the turn scope can never be consumed by other tools or flows.
type SendScopeProvider interface {
	SendScope(ctx context.Context, agentID string) (messagesend.Scope, bool)
}

// MessageRuntime is the exact daemon runtime surface consumed by message
// tools.
type MessageRuntime interface {
	Session(string) (types.SessionSnapshot, bool)
	PrepareVisibleReply(string, ...string) string
	ConfirmReactionReply(string, string, string) bool
	ReconcileInboxMessages(string, []string) error
	ReconcileRuntimeAckedDeliveries(string, []string) ([]string, error)
	ReconcileDeferredDeliveriesForAgent(string, []string) ([]string, error)
	PrepareRuntimeMessageToolDeliveries(string, []types.InboxMessage) int
	AcknowledgeRuntimeMessageToolDeliveriesForSession(string, []string, types.MCPToolDeliveryOutcome, string)
	InboxMessagesForFreshnessPage(string, string, int64, int) (types.InboxReadPage, error)
	InboxMessagesForFreshnessPageForRuntime(string, string, int64, int, string) (types.InboxReadPage, error)
	PendingRuntimeInputCoversFreshness(string, string, int64) bool
	MessageFreshnessSnapshot(string, string) (types.MessageFreshnessFrontier, bool)
	RememberMessageFreshnessObserved(string, []types.InboxMessage) bool
	RememberMessageFreshnessHeld(string, string, int64, int64) bool
	SetActiveTaskTarget(string, types.ActiveTaskTarget) bool
}

// targetAwareVisibleReplyConfirmer is implemented by the production runtime
// bridge. It settles acknowledged visible-reply obligations by resolved target
// and the server's SeenUpToSeq frontier, rather than by local delivery tokens.
type targetAwareVisibleReplyConfirmer interface {
	ConfirmVisibleReplyForTarget(agentID, targetKey string, seenUpToSeq int64, replyMessageID string) bool
}

// NewMessageHandler creates a message handler with all required production
// dependencies.
func NewMessageHandler(service *MessageService, config MessageHandlerConfig) (types.MCPHandler, error) {
	if service == nil {
		return nil, errors.New("message service is required")
	}
	if config.Connection == nil {
		return nil, errors.New("message connection is required")
	}
	if config.Runtime == nil {
		return nil, errors.New("message runtime is required")
	}
	if config.SendScopeProvider == nil {
		return nil, errors.New("send scope provider is required")
	}
	return &mcpHandler{
		c:         config.Connection,
		runtime:   config.Runtime,
		service:   service,
		sendScope: config.SendScopeProvider,
	}, nil
}

func newMessageInventoryHandler() *mcpHandler {
	return &mcpHandler{}
}

func (s *MessageService) newMessageHandler(config MessageHandlerConfig) (*mcpHandler, error) {
	if s == nil {
		return nil, errors.New("message service is required")
	}
	handler, err := NewMessageHandler(s, config)
	if err != nil {
		return nil, err
	}
	return handler.(*mcpHandler), nil
}

// mcpHandler implements types.MCPHandler for AgentTeam sessions,
// providing core tools plus AgentTeam-specific tools.
type mcpHandler struct {
	c         AgentTeamConnection
	runtime   MessageRuntime
	service   *MessageService
	sendScope SendScopeProvider
}

func (h *mcpHandler) Tools() []types.MCPToolDef {
	return h.toolDefs()
}

// apiDispatch resolves the AgentTeam API for the agent session and delegates
// to fn. Returns an error if the API is not available.
func (h *mcpHandler) apiDispatch(ctx context.Context, agentID string, args json.RawMessage, fn func(context.Context, *apiclient.Client, json.RawMessage) *types.MCPToolResult) *types.MCPToolResult {
	api := h.apiClient(agentID)
	if api == nil {
		return toolError("agentteam API not available for this session")
	}
	api.LocalAgentID = strings.TrimSpace(agentID)
	return fn(ctx, api, args)
}

func (h *mcpHandler) apiClient(agentID string) *apiclient.Client {
	if h.c == nil || h.runtime == nil {
		return nil
	}
	sess, ok := h.runtime.Session(agentID)
	if !ok {
		return nil
	}
	return h.c.APIClient(sess.ServerAgentID)
}

func (h *mcpHandler) recordSent() {
	if h == nil || h.c == nil {
		return
	}
	if collector := metrics.DefaultServerMessageCollector(); collector != nil {
		collector.RecordSent(h.c.ID())
	}
}

func (h *mcpHandler) recordAgentSent(agentID string) {
	if collector := metrics.DefaultServerMessageCollector(); collector != nil {
		collector.RecordAgentSent(agentID)
	}
}
