package plan

import (
	"fmt"
	"strings"
)

// MaxSendMessageRelatedScopes bounds callback-context fan-in and the
// authorization query/prompt footprint for one Agent step.
const MaxSendMessageRelatedScopes = 64

func documentRelatedScopeBudgetDiagnostics(document *Document) []Diagnostic {
	diagnostics := make([]Diagnostic, 0)
	nodeCount := min(len(document.Graph.Nodes), MaxGraphNodes)
	for index := 0; index < nodeCount; index++ {
		config := document.Graph.Nodes[index].SendMessage
		if config == nil || len(config.RelatedScopes.Items) <= MaxSendMessageRelatedScopes {
			continue
		}
		diagnostics = append(diagnostics, Diagnostic{
			Severity: SeverityError,
			Code:     "resource.too_many_related_scopes",
			Path:     fmt.Sprintf("graph.nodes[%d].sendMessage.relatedScopes", index),
			Message:  fmt.Sprintf("relatedScopes must not contain more than %d scopes", MaxSendMessageRelatedScopes),
			NodeID:   document.Graph.Nodes[index].ID,
		})
	}
	return diagnostics
}

type pendingLiteralValue struct {
	value any
	depth int
}

func appendSendMessageLiteralValues(pending []pendingLiteralValue, config *SendMessageConfig) []pendingLiteralValue {
	if config == nil {
		return pending
	}
	if config.WorkScope != nil && config.WorkScope.Value != nil {
		pending = append(pending, pendingLiteralValue{value: *config.WorkScope.Value})
	}
	if fixed := config.Responder.fixed(); fixed != nil && fixed.Value != nil {
		pending = append(pending, pendingLiteralValue{value: *fixed.Value})
	}
	if selected := config.Responder.selectOne(); selected != nil && selected.Value != nil {
		pending = append(pending, pendingLiteralValue{value: *selected.Value})
	}
	for _, relatedScope := range config.RelatedScopes.Items {
		if relatedScope.Value != nil {
			pending = append(pending, pendingLiteralValue{value: *relatedScope.Value})
		}
	}
	if config.RelatedScopes.Array != nil && config.RelatedScopes.Array.Value != nil {
		pending = append(pending, pendingLiteralValue{value: *config.RelatedScopes.Array.Value})
	}
	if config.WorkItems != nil && config.WorkItems.AllowedResponders.Value != nil {
		pending = append(pending, pendingLiteralValue{value: *config.WorkItems.AllowedResponders.Value})
	}
	return pending
}

func (projection *artifactMaterializationProjection) addSendMessageConfig(
	config *SendMessageConfig,
	callbackOutput artifactSchemaProjection,
) {
	if config.Completion == CompletionCallback {
		projection.addContract(callbackOutput, 1)
	}
	if config.hasWorkScope() {
		projection.addContract(artifactSchemaProjection{fields: 1, units: 1, bytes: artifactSchemaRecordBytes}, 1)
	}
	if config.WorkItems != nil {
		projection.addContract(artifactSchemaProjection{fields: 1, units: 1, bytes: artifactSchemaRecordBytes}, 1)
	}
	if config.StartThread {
		projection.addContract(artifactSchemaProjection{fields: 2, units: 2, bytes: 2 * artifactSchemaRecordBytes}, 1)
	}
	projection.addContract(callbackOutput, 1) // config callback contract
	if strings.TrimSpace(config.Target) != "" {
		projection.addTemplate(config.Target)
	}
	if config.Scope != nil {
		projection.addValueExpression(*config.Scope)
	}
	if fixed := config.Responder.fixed(); fixed != nil {
		projection.addValueExpression(*fixed)
	}
	if selected := config.Responder.selectOne(); selected != nil {
		projection.addValueExpression(*selected)
	}
	if config.WorkScope != nil {
		projection.addValueExpression(*config.WorkScope)
	}
	for _, relatedScope := range config.RelatedScopes.Items {
		projection.addValueExpression(relatedScope)
	}
	if config.RelatedScopes.Array != nil {
		projection.addValueExpression(*config.RelatedScopes.Array)
	}
	if config.WorkItems != nil {
		projection.addValueExpression(config.WorkItems.AllowedResponders)
	}
	projection.addTemplate(config.Text)
}
