package core

import (
	"context"

	"ragflow/internal/harness/core/schema"
)

// ---- NewEventSenderModelWrapper creates a handler that sends model output events.
// Place this in the Handlers chain to control WHERE events are emitted:
// - Innermost position (last in Handlers list): events contain original (unmodified) model output
// - Outermost position (first in Handlers list): events contain fully processed output
//
// When detected in Handlers, the framework skips its built-in event sender to avoid duplicates.
func NewEventSenderModelWrapper[M MessageType]() *eventSenderModelHandler[M] {
	return &eventSenderModelHandler[M]{}
}

type eventSenderModelHandler[M MessageType] struct{}

func (h *eventSenderModelHandler[M]) WrapModel(ctx context.Context, m Model[M], mc *TypedModelContext[M]) (Model[M], error) {
	ec := getReActExecCtx[M](ctx)
	if ec == nil {
		return m, nil
	}
	return wrapModelWithEventSender(m, ec), nil
}

// All other middleware methods are no-op
func (h *eventSenderModelHandler[M]) BeforeAgent(ctx context.Context, rc *ReActAgentContext) (context.Context, *ReActAgentContext, error) {
	return ctx, rc, nil
}
func (h *eventSenderModelHandler[M]) AfterAgent(ctx context.Context, state *TypedReActAgentState[M]) (context.Context, error) {
	return ctx, nil
}
func (h *eventSenderModelHandler[M]) BeforeModelRewrite(ctx context.Context, state *TypedReActAgentState[M], mc *TypedModelContext[M]) (context.Context, *TypedReActAgentState[M], error) {
	return ctx, state, nil
}
func (h *eventSenderModelHandler[M]) AfterModelRewrite(ctx context.Context, state *TypedReActAgentState[M], mc *TypedModelContext[M]) (context.Context, *TypedReActAgentState[M], error) {
	return ctx, state, nil
}

// HasUserEventSenderModelWrapper checks if the handlers list contains a user-provided
// NewEventSenderModelWrapper. When present, the framework skips its internal default
// model event sender to avoid duplicate events.
func HasUserEventSenderModelWrapper[M MessageType](handlers []TypedReActMiddleware[M]) bool {
	for _, h := range handlers {
		if _, ok := h.(*eventSenderModelHandler[M]); ok {
			return true
		}
	}
	return false
}

// ---- Tool event constructors ----

// TypedToolInvokeEvent creates an event for a synchronous tool result.
func TypedToolInvokeEvent(result string, tc *ToolContext) *TypedAgentEvent[*schema.Message] {
	msg := schema.ToolMessage(result, tc.CallID)
	return typedEventFromMessage(msg, nil, schema.RoleTool, tc.Name)
}

// TypedToolStreamEvent creates an event for a streaming tool result.
func TypedToolStreamEvent(resultChunks []string, tc *ToolContext) *TypedAgentEvent[*schema.Message] {
	content := ""
	for _, ch := range resultChunks {
		content += ch
	}
	msg := schema.ToolMessage(content, tc.CallID)
	return typedEventFromMessage(msg, nil, schema.RoleTool, tc.Name)
}

// TypedEnhancedToolInvokeEvent creates an event for an enhanced tool result.
// Propagates Extra metadata for multimodal support.
func TypedEnhancedToolInvokeEvent(result *schema.ToolResult, tc *ToolContext) *TypedAgentEvent[*schema.Message] {
	content := result.Content
	if content == "" {
		content = result.Error
	}
	msg := schema.ToolMessage(content, tc.CallID)
	msg.Name = tc.Name
	if result.Extra != nil {
		if msg.Extra == nil {
			msg.Extra = make(map[string]any, len(result.Extra))
		}
		for k, v := range result.Extra {
			msg.Extra[k] = v
		}
	}
	return typedEventFromMessage(msg, nil, schema.RoleTool, tc.Name)
}

// TypedEnhancedToolStreamEvent creates an event for a streaming enhanced tool result.
// Propagates the last result's Extra metadata.
func TypedEnhancedToolStreamEvent(results []*schema.ToolResult, tc *ToolContext) *TypedAgentEvent[*schema.Message] {
	if len(results) == 0 {
		return nil
	}
	last := results[len(results)-1]
	content := last.Content
	if content == "" {
		content = last.Error
	}
	msg := schema.ToolMessage(content, tc.CallID)
	msg.Name = tc.Name
	if last.Extra != nil {
		if msg.Extra == nil {
			msg.Extra = make(map[string]any, len(last.Extra))
		}
		for k, v := range last.Extra {
			msg.Extra[k] = v
		}
	}
	return typedEventFromMessage(msg, nil, schema.RoleTool, tc.Name)
}
